summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/rubygems
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rubygems
Fresh start
Diffstat (limited to 'jni/ruby/lib/rubygems')
-rw-r--r--jni/ruby/lib/rubygems/LICENSE.txt54
-rw-r--r--jni/ruby/lib/rubygems/available_set.rb164
-rw-r--r--jni/ruby/lib/rubygems/basic_specification.rb290
-rw-r--r--jni/ruby/lib/rubygems/command.rb582
-rw-r--r--jni/ruby/lib/rubygems/command_manager.rb218
-rw-r--r--jni/ruby/lib/rubygems/commands/build_command.rb60
-rw-r--r--jni/ruby/lib/rubygems/commands/cert_command.rb276
-rw-r--r--jni/ruby/lib/rubygems/commands/check_command.rb93
-rw-r--r--jni/ruby/lib/rubygems/commands/cleanup_command.rb165
-rw-r--r--jni/ruby/lib/rubygems/commands/contents_command.rb190
-rw-r--r--jni/ruby/lib/rubygems/commands/dependency_command.rb207
-rw-r--r--jni/ruby/lib/rubygems/commands/environment_command.rb158
-rw-r--r--jni/ruby/lib/rubygems/commands/fetch_command.rb77
-rw-r--r--jni/ruby/lib/rubygems/commands/generate_index_command.rb84
-rw-r--r--jni/ruby/lib/rubygems/commands/help_command.rb384
-rw-r--r--jni/ruby/lib/rubygems/commands/install_command.rb347
-rw-r--r--jni/ruby/lib/rubygems/commands/list_command.rb40
-rw-r--r--jni/ruby/lib/rubygems/commands/lock_command.rb110
-rw-r--r--jni/ruby/lib/rubygems/commands/mirror_command.rb25
-rw-r--r--jni/ruby/lib/rubygems/commands/open_command.rb74
-rw-r--r--jni/ruby/lib/rubygems/commands/outdated_command.rb32
-rw-r--r--jni/ruby/lib/rubygems/commands/owner_command.rb99
-rw-r--r--jni/ruby/lib/rubygems/commands/pristine_command.rb165
-rw-r--r--jni/ruby/lib/rubygems/commands/push_command.rb98
-rw-r--r--jni/ruby/lib/rubygems/commands/query_command.rb343
-rw-r--r--jni/ruby/lib/rubygems/commands/rdoc_command.rb96
-rw-r--r--jni/ruby/lib/rubygems/commands/search_command.rb40
-rw-r--r--jni/ruby/lib/rubygems/commands/server_command.rb86
-rw-r--r--jni/ruby/lib/rubygems/commands/setup_command.rb483
-rw-r--r--jni/ruby/lib/rubygems/commands/sources_command.rb210
-rw-r--r--jni/ruby/lib/rubygems/commands/specification_command.rb145
-rw-r--r--jni/ruby/lib/rubygems/commands/stale_command.rb38
-rw-r--r--jni/ruby/lib/rubygems/commands/uninstall_command.rb164
-rw-r--r--jni/ruby/lib/rubygems/commands/unpack_command.rb182
-rw-r--r--jni/ruby/lib/rubygems/commands/update_command.rb277
-rw-r--r--jni/ruby/lib/rubygems/commands/which_command.rb90
-rw-r--r--jni/ruby/lib/rubygems/commands/yank_command.rb107
-rw-r--r--jni/ruby/lib/rubygems/compatibility.rb59
-rw-r--r--jni/ruby/lib/rubygems/config_file.rb483
-rw-r--r--jni/ruby/lib/rubygems/core_ext/kernel_gem.rb73
-rwxr-xr-xjni/ruby/lib/rubygems/core_ext/kernel_require.rb139
-rw-r--r--jni/ruby/lib/rubygems/defaults.rb177
-rw-r--r--jni/ruby/lib/rubygems/dependency.rb334
-rw-r--r--jni/ruby/lib/rubygems/dependency_installer.rb480
-rw-r--r--jni/ruby/lib/rubygems/dependency_list.rb240
-rw-r--r--jni/ruby/lib/rubygems/deprecate.rb70
-rw-r--r--jni/ruby/lib/rubygems/doctor.rb131
-rw-r--r--jni/ruby/lib/rubygems/errors.rb137
-rw-r--r--jni/ruby/lib/rubygems/exceptions.rb270
-rw-r--r--jni/ruby/lib/rubygems/ext.rb18
-rw-r--r--jni/ruby/lib/rubygems/ext/build_error.rb6
-rw-r--r--jni/ruby/lib/rubygems/ext/builder.rb218
-rw-r--r--jni/ruby/lib/rubygems/ext/cmake_builder.rb16
-rw-r--r--jni/ruby/lib/rubygems/ext/configure_builder.rb23
-rw-r--r--jni/ruby/lib/rubygems/ext/ext_conf_builder.rb78
-rw-r--r--jni/ruby/lib/rubygems/ext/rake_builder.rb36
-rw-r--r--jni/ruby/lib/rubygems/gem_runner.rb81
-rw-r--r--jni/ruby/lib/rubygems/gemcutter_utilities.rb154
-rw-r--r--jni/ruby/lib/rubygems/indexer.rb498
-rw-r--r--jni/ruby/lib/rubygems/install_default_message.rb12
-rw-r--r--jni/ruby/lib/rubygems/install_message.rb12
-rw-r--r--jni/ruby/lib/rubygems/install_update_options.rb186
-rw-r--r--jni/ruby/lib/rubygems/installer.rb819
-rw-r--r--jni/ruby/lib/rubygems/installer_test_case.rb193
-rw-r--r--jni/ruby/lib/rubygems/local_remote_options.rb148
-rw-r--r--jni/ruby/lib/rubygems/mock_gem_ui.rb88
-rw-r--r--jni/ruby/lib/rubygems/name_tuple.rb123
-rw-r--r--jni/ruby/lib/rubygems/package.rb613
-rw-r--r--jni/ruby/lib/rubygems/package/digest_io.rb64
-rw-r--r--jni/ruby/lib/rubygems/package/file_source.rb33
-rw-r--r--jni/ruby/lib/rubygems/package/io_source.rb45
-rw-r--r--jni/ruby/lib/rubygems/package/old.rb178
-rw-r--r--jni/ruby/lib/rubygems/package/source.rb3
-rw-r--r--jni/ruby/lib/rubygems/package/tar_header.rb229
-rw-r--r--jni/ruby/lib/rubygems/package/tar_reader.rb123
-rw-r--r--jni/ruby/lib/rubygems/package/tar_reader/entry.rb147
-rw-r--r--jni/ruby/lib/rubygems/package/tar_test_case.rb137
-rw-r--r--jni/ruby/lib/rubygems/package/tar_writer.rb326
-rw-r--r--jni/ruby/lib/rubygems/package_task.rb128
-rw-r--r--jni/ruby/lib/rubygems/path_support.rb87
-rw-r--r--jni/ruby/lib/rubygems/platform.rb204
-rw-r--r--jni/ruby/lib/rubygems/psych_additions.rb9
-rw-r--r--jni/ruby/lib/rubygems/psych_tree.rb31
-rw-r--r--jni/ruby/lib/rubygems/rdoc.rb335
-rw-r--r--jni/ruby/lib/rubygems/remote_fetcher.rb404
-rw-r--r--jni/ruby/lib/rubygems/request.rb244
-rw-r--r--jni/ruby/lib/rubygems/request/connection_pools.rb83
-rw-r--r--jni/ruby/lib/rubygems/request/http_pool.rb47
-rw-r--r--jni/ruby/lib/rubygems/request/https_pool.rb10
-rw-r--r--jni/ruby/lib/rubygems/request_set.rb413
-rw-r--r--jni/ruby/lib/rubygems/request_set/gem_dependency_api.rb801
-rw-r--r--jni/ruby/lib/rubygems/request_set/lockfile.rb650
-rw-r--r--jni/ruby/lib/rubygems/requirement.rb273
-rw-r--r--jni/ruby/lib/rubygems/resolver.rb485
-rw-r--r--jni/ruby/lib/rubygems/resolver/activation_request.rb172
-rw-r--r--jni/ruby/lib/rubygems/resolver/api_set.rb125
-rw-r--r--jni/ruby/lib/rubygems/resolver/api_specification.rb85
-rw-r--r--jni/ruby/lib/rubygems/resolver/best_set.rb78
-rw-r--r--jni/ruby/lib/rubygems/resolver/composed_set.rb66
-rw-r--r--jni/ruby/lib/rubygems/resolver/conflict.rb160
-rw-r--r--jni/ruby/lib/rubygems/resolver/current_set.rb13
-rw-r--r--jni/ruby/lib/rubygems/resolver/dependency_request.rb116
-rw-r--r--jni/ruby/lib/rubygems/resolver/git_set.rb122
-rw-r--r--jni/ruby/lib/rubygems/resolver/git_specification.rb59
-rw-r--r--jni/ruby/lib/rubygems/resolver/index_set.rb80
-rw-r--r--jni/ruby/lib/rubygems/resolver/index_specification.rb69
-rw-r--r--jni/ruby/lib/rubygems/resolver/installed_specification.rb58
-rw-r--r--jni/ruby/lib/rubygems/resolver/installer_set.rb224
-rw-r--r--jni/ruby/lib/rubygems/resolver/local_specification.rb41
-rw-r--r--jni/ruby/lib/rubygems/resolver/lock_set.rb84
-rw-r--r--jni/ruby/lib/rubygems/resolver/lock_specification.rb84
-rw-r--r--jni/ruby/lib/rubygems/resolver/requirement_list.rb81
-rw-r--r--jni/ruby/lib/rubygems/resolver/set.rb56
-rw-r--r--jni/ruby/lib/rubygems/resolver/spec_specification.rb56
-rw-r--r--jni/ruby/lib/rubygems/resolver/specification.rb110
-rw-r--r--jni/ruby/lib/rubygems/resolver/stats.rb44
-rw-r--r--jni/ruby/lib/rubygems/resolver/vendor_set.rb87
-rw-r--r--jni/ruby/lib/rubygems/resolver/vendor_specification.rb24
-rw-r--r--jni/ruby/lib/rubygems/security.rb595
-rw-r--r--jni/ruby/lib/rubygems/security/policies.rb115
-rw-r--r--jni/ruby/lib/rubygems/security/policy.rb295
-rw-r--r--jni/ruby/lib/rubygems/security/signer.rb154
-rw-r--r--jni/ruby/lib/rubygems/security/trust_dir.rb118
-rw-r--r--jni/ruby/lib/rubygems/server.rb868
-rw-r--r--jni/ruby/lib/rubygems/source.rb234
-rw-r--r--jni/ruby/lib/rubygems/source/git.rb240
-rw-r--r--jni/ruby/lib/rubygems/source/installed.rb40
-rw-r--r--jni/ruby/lib/rubygems/source/local.rb129
-rw-r--r--jni/ruby/lib/rubygems/source/lock.rb48
-rw-r--r--jni/ruby/lib/rubygems/source/specific_file.rb72
-rw-r--r--jni/ruby/lib/rubygems/source/vendor.rb27
-rw-r--r--jni/ruby/lib/rubygems/source_list.rb149
-rw-r--r--jni/ruby/lib/rubygems/source_local.rb5
-rw-r--r--jni/ruby/lib/rubygems/source_specific_file.rb4
-rw-r--r--jni/ruby/lib/rubygems/spec_fetcher.rb269
-rw-r--r--jni/ruby/lib/rubygems/specification.rb2800
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/.document1
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem25
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem32
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem14
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem23
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem28
-rw-r--r--jni/ruby/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem20
-rw-r--r--jni/ruby/lib/rubygems/stub_specification.rb196
-rw-r--r--jni/ruby/lib/rubygems/syck_hack.rb76
-rw-r--r--jni/ruby/lib/rubygems/test_case.rb1430
-rw-r--r--jni/ruby/lib/rubygems/test_utilities.rb383
-rw-r--r--jni/ruby/lib/rubygems/text.rb75
-rw-r--r--jni/ruby/lib/rubygems/uninstaller.rb345
-rw-r--r--jni/ruby/lib/rubygems/uri_formatter.rb49
-rw-r--r--jni/ruby/lib/rubygems/user_interaction.rb711
-rw-r--r--jni/ruby/lib/rubygems/util.rb134
-rw-r--r--jni/ruby/lib/rubygems/util/list.rb48
-rw-r--r--jni/ruby/lib/rubygems/util/stringio.rb34
-rw-r--r--jni/ruby/lib/rubygems/validator.rb165
-rw-r--r--jni/ruby/lib/rubygems/version.rb356
-rw-r--r--jni/ruby/lib/rubygems/version_option.rb71
157 files changed, 30129 insertions, 0 deletions
diff --git a/jni/ruby/lib/rubygems/LICENSE.txt b/jni/ruby/lib/rubygems/LICENSE.txt
new file mode 100644
index 0000000..8a0a51d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/LICENSE.txt
@@ -0,0 +1,54 @@
+RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
+Weirich and others. You can redistribute it and/or modify it under
+either the terms of the MIT license (see the file MIT.txt), or the
+conditions below:
+
+1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
+
+2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
+
+ a. place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
+
+ b. use the modified software only within your corporation or
+ organization.
+
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d. make other distribution arrangements with the author.
+
+3. You may distribute the software in object code or executable
+ form, provided that you do at least ONE of the following:
+
+ a. distribute the executables and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
+
+ b. accompany the distribution with the machine-readable source of
+ the software.
+
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d. make other distribution arrangements with the author.
+
+4. You may modify and include the part of the software into any other
+ software (possibly commercial).
+
+5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
+
+6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
+
diff --git a/jni/ruby/lib/rubygems/available_set.rb b/jni/ruby/lib/rubygems/available_set.rb
new file mode 100644
index 0000000..dae254b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/available_set.rb
@@ -0,0 +1,164 @@
+class Gem::AvailableSet
+
+ include Enumerable
+
+ Tuple = Struct.new(:spec, :source)
+
+ attr_accessor :remote # :nodoc:
+
+ def initialize
+ @set = []
+ @sorted = nil
+ @remote = true
+ end
+
+ attr_reader :set
+
+ def add(spec, source)
+ @set << Tuple.new(spec, source)
+ @sorted = nil
+ self
+ end
+
+ def <<(o)
+ case o
+ when Gem::AvailableSet
+ s = o.set
+ when Array
+ s = o.map do |sp,so|
+ if !sp.kind_of?(Gem::Specification) or !so.kind_of?(Gem::Source)
+ raise TypeError, "Array must be in [[spec, source], ...] form"
+ end
+
+ Tuple.new(sp,so)
+ end
+ else
+ raise TypeError, "must be a Gem::AvailableSet"
+ end
+
+ @set += s
+ @sorted = nil
+
+ self
+ end
+
+ ##
+ # Yields each Tuple in this AvailableSet
+
+ def each
+ return enum_for __method__ unless block_given?
+
+ @set.each do |tuple|
+ yield tuple
+ end
+ end
+
+ ##
+ # Yields the Gem::Specification for each Tuple in this AvailableSet
+
+ def each_spec
+ return enum_for __method__ unless block_given?
+
+ each do |tuple|
+ yield tuple.spec
+ end
+ 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
+
+ ##
+ # Converts this AvailableSet into a RequestSet that can be used to install
+ # gems.
+ #
+ # If +development+ is :none then no development dependencies are installed.
+ # Other options are :shallow for only direct development dependencies of the
+ # gems in this set or :all for all development dependencies.
+
+ def to_request_set development = :none
+ request_set = Gem::RequestSet.new
+ request_set.development = :all == development
+
+ each_spec do |spec|
+ request_set.always_install << spec
+
+ request_set.gem spec.name, spec.version
+ request_set.import spec.development_dependencies if
+ :shallow == development
+ end
+
+ request_set
+ end
+
+ ##
+ #
+ # Used by the Resolver, the protocol to use a AvailableSet as a
+ # search Set.
+
+ def find_all(req)
+ dep = req.dependency
+
+ match = @set.find_all do |t|
+ dep.match? t.spec
+ end
+
+ match.map do |t|
+ Gem::Resolver::LocalSpecification.new(self, t.spec, t.source)
+ end
+ end
+
+ def prefetch(reqs)
+ 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
diff --git a/jni/ruby/lib/rubygems/basic_specification.rb b/jni/ruby/lib/rubygems/basic_specification.rb
new file mode 100644
index 0000000..f5fb0f5
--- /dev/null
+++ b/jni/ruby/lib/rubygems/basic_specification.rb
@@ -0,0 +1,290 @@
+##
+# BasicSpecification is an abstract class which implements some common code
+# used by both Specification and StubSpecification.
+
+class Gem::BasicSpecification
+
+ ##
+ # Allows installation of extensions for git: gems.
+
+ attr_writer :base_dir # :nodoc:
+
+ ##
+ # Sets the directory where extensions for this gem will be installed.
+
+ attr_writer :extension_dir # :nodoc:
+
+ ##
+ # Is this specification ignored for activation purposes?
+
+ attr_writer :ignored # :nodoc:
+
+ ##
+ # The path this gemspec was loaded from. This attribute is not persisted.
+
+ attr_reader :loaded_from
+
+ ##
+ # Allows correct activation of git: and path: gems.
+
+ attr_writer :full_gem_path # :nodoc:
+
+ def self.default_specifications_dir
+ File.join(Gem.default_dir, "specifications", "default")
+ end
+
+ ##
+ # True when the gem has been activated
+
+ def activated?
+ raise NotImplementedError
+ end
+
+ ##
+ # Returns the full path to the base gem directory.
+ #
+ # eg: /usr/local/lib/ruby/gems/1.8
+
+ def base_dir
+ return Gem.dir unless loaded_from
+ @base_dir ||= if default_gem? then
+ File.dirname File.dirname File.dirname loaded_from
+ else
+ File.dirname File.dirname loaded_from
+ end
+ end
+
+ ##
+ # Return true if this spec can require +file+.
+
+ def contains_requirable_file? file
+ @contains_requirable_file ||= {}
+ @contains_requirable_file[file] ||=
+ begin
+ if instance_variable_defined?(:@ignored) or
+ instance_variable_defined?('@ignored') then
+ return false
+ elsif missing_extensions? then
+ @ignored = true
+
+ warn "Ignoring #{full_name} because its extensions are not built. " +
+ "Try: gem pristine #{name} --version #{version}"
+ return false
+ end
+
+ suffixes = Gem.suffixes
+
+ full_require_paths.any? do |dir|
+ base = "#{dir}/#{file}"
+ suffixes.any? { |suf| File.file? "#{base}#{suf}" }
+ end
+ end ? :yes : :no
+ @contains_requirable_file[file] == :yes
+ end
+
+ def default_gem?
+ loaded_from &&
+ File.dirname(loaded_from) == self.class.default_specifications_dir
+ end
+
+ ##
+ # Returns full path to the directory where gem's extensions are installed.
+
+ def extension_dir
+ @extension_dir ||= File.expand_path File.join(extensions_dir, full_name)
+ end
+
+ ##
+ # Returns path to the extensions directory.
+
+ def extensions_dir
+ @extensions_dir ||= Gem.default_ext_dir_for(base_dir) ||
+ File.join(base_dir, 'extensions', Gem::Platform.local.to_s,
+ Gem.extension_api_version)
+ end
+
+ def find_full_gem_path # :nodoc:
+ # TODO: also, shouldn't it default to full_name if it hasn't been written?
+ path = File.expand_path File.join(gems_dir, full_name)
+ path.untaint
+ path if File.directory? path
+ end
+
+ private :find_full_gem_path
+
+ ##
+ # The full path to the gem (install path + full name).
+
+ def full_gem_path
+ # TODO: This is a heavily used method by gems, so we'll need
+ # to aleast just alias it to #gem_dir rather than remove it.
+ @full_gem_path ||= find_full_gem_path
+ end
+
+ ##
+ # Returns the full name (name-version) of this Gem. Platform information
+ # is included (name-version-platform) if it is specified and not the
+ # default Ruby platform.
+
+ def full_name
+ if platform == Gem::Platform::RUBY or platform.nil? then
+ "#{name}-#{version}".untaint
+ else
+ "#{name}-#{version}-#{platform}".untaint
+ end
+ end
+
+ ##
+ # Full paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
+ # activated.
+
+ def full_require_paths
+ @full_require_paths ||=
+ begin
+ full_paths = raw_require_paths.map do |path|
+ File.join full_gem_path, path
+ end
+
+ full_paths.unshift extension_dir unless @extensions.nil? || @extensions.empty?
+
+ full_paths
+ end
+ end
+
+ ##
+ # Full path of the target library file.
+ # If the file is not in this gem, return nil.
+
+ def to_fullpath path
+ if activated? then
+ @paths_map ||= {}
+ @paths_map[path] ||=
+ begin
+ fullpath = nil
+ suffixes = Gem.suffixes
+ full_require_paths.find do |dir|
+ suffixes.find do |suf|
+ File.file?(fullpath = "#{dir}/#{path}#{suf}")
+ end
+ end ? fullpath : nil
+ end
+ else
+ nil
+ end
+ end
+
+ ##
+ # Returns the full path to this spec's gem directory.
+ # eg: /usr/local/lib/ruby/1.8/gems/mygem-1.0
+
+ def gem_dir
+ @gem_dir ||= File.expand_path File.join(gems_dir, full_name)
+ end
+
+ ##
+ # Returns the full path to the gems directory containing this spec's
+ # gem directory. eg: /usr/local/lib/ruby/1.8/gems
+
+ def gems_dir
+ # TODO: this logic seems terribly broken, but tests fail if just base_dir
+ @gems_dir ||= File.join(loaded_from && base_dir || Gem.dir, "gems")
+ end
+
+ ##
+ # Set the path the Specification was loaded from. +path+ is converted to a
+ # String.
+
+ def loaded_from= path
+ @loaded_from = path && path.to_s
+
+ @extension_dir = nil
+ @extensions_dir = nil
+ @full_gem_path = nil
+ @gem_dir = nil
+ @gems_dir = nil
+ @base_dir = nil
+ end
+
+ ##
+ # Name of the gem
+
+ def name
+ raise NotImplementedError
+ end
+
+ ##
+ # Platform of the gem
+
+ def platform
+ raise NotImplementedError
+ end
+
+ def raw_require_paths # :nodoc:
+ Array(@require_paths)
+ end
+
+ ##
+ # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
+ # activated.
+ #
+ # See also #require_paths=
+ #
+ # If you have an extension you do not need to add <code>"ext"</code> to the
+ # require path, the extension build process will copy the extension files
+ # into "lib" for you.
+ #
+ # The default value is <code>"lib"</code>
+ #
+ # Usage:
+ #
+ # # If all library files are in the root directory...
+ # spec.require_path = '.'
+
+ def require_paths
+ return raw_require_paths if @extensions.nil? || @extensions.empty?
+
+ [extension_dir].concat raw_require_paths
+ end
+
+ ##
+ # Returns the paths to the source files for use with analysis and
+ # documentation tools. These paths are relative to full_gem_path.
+
+ def source_paths
+ paths = raw_require_paths.dup
+
+ if @extensions then
+ ext_dirs = @extensions.map do |extension|
+ extension.split(File::SEPARATOR, 2).first
+ end.uniq
+
+ paths.concat ext_dirs
+ end
+
+ paths.uniq
+ end
+
+ ##
+ # Return a Gem::Specification from this gem
+
+ def to_spec
+ raise NotImplementedError
+ end
+
+ ##
+ # Version of the gem
+
+ def version
+ raise NotImplementedError
+ end
+
+ ##
+ # Whether this specification is stubbed - i.e. we have information
+ # about the gem from a stub line, without having to evaluate the
+ # entire gemspec file.
+ def stubbed?
+ raise NotImplementedError
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/command.rb b/jni/ruby/lib/rubygems/command.rb
new file mode 100644
index 0000000..0c6abec
--- /dev/null
+++ b/jni/ruby/lib/rubygems/command.rb
@@ -0,0 +1,582 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'optparse'
+require 'rubygems/requirement'
+require 'rubygems/user_interaction'
+
+##
+# Base class for all Gem commands. When creating a new gem command, define
+# #initialize, #execute, #arguments, #defaults_str, #description and #usage
+# (as appropriate). See the above mentioned methods for details.
+#
+# A very good example to look at is Gem::Commands::ContentsCommand
+
+class Gem::Command
+
+ include Gem::UserInteraction
+
+ ##
+ # The name of the command.
+
+ attr_reader :command
+
+ ##
+ # The options for the command.
+
+ attr_reader :options
+
+ ##
+ # The default options for the command.
+
+ attr_accessor :defaults
+
+ ##
+ # The name of the command for command-line invocation.
+
+ attr_accessor :program_name
+
+ ##
+ # A short description of the command.
+
+ attr_accessor :summary
+
+ ##
+ # Arguments used when building gems
+
+ def self.build_args
+ @build_args ||= []
+ end
+
+ def self.build_args=(value)
+ @build_args = value
+ end
+
+ def self.common_options
+ @common_options ||= []
+ end
+
+ def self.add_common_option(*args, &handler)
+ Gem::Command.common_options << [args, handler]
+ end
+
+ def self.extra_args
+ @extra_args ||= []
+ end
+
+ def self.extra_args=(value)
+ case value
+ when Array
+ @extra_args = value
+ when String
+ @extra_args = value.split
+ end
+ end
+
+ ##
+ # Return an array of extra arguments for the command. The extra arguments
+ # come from the gem configuration file read at program startup.
+
+ def self.specific_extra_args(cmd)
+ specific_extra_args_hash[cmd]
+ end
+
+ ##
+ # Add a list of extra arguments for the given command. +args+ may be an
+ # array or a string to be split on white space.
+
+ def self.add_specific_extra_args(cmd,args)
+ args = args.split(/\s+/) if args.kind_of? String
+ specific_extra_args_hash[cmd] = args
+ end
+
+ ##
+ # Accessor for the specific extra args hash (self initializing).
+
+ def self.specific_extra_args_hash
+ @specific_extra_args_hash ||= Hash.new do |h,k|
+ h[k] = Array.new
+ end
+ end
+
+ ##
+ # Initializes a generic gem command named +command+. +summary+ is a short
+ # description displayed in `gem help commands`. +defaults+ are the default
+ # options. Defaults should be mirrored in #defaults_str, unless there are
+ # none.
+ #
+ # When defining a new command subclass, use add_option to add command-line
+ # switches.
+ #
+ # Unhandled arguments (gem names, files, etc.) are left in
+ # <tt>options[:args]</tt>.
+
+ def initialize(command, summary=nil, defaults={})
+ @command = command
+ @summary = summary
+ @program_name = "gem #{command}"
+ @defaults = defaults
+ @options = defaults.dup
+ @option_groups = Hash.new { |h,k| h[k] = [] }
+ @parser = nil
+ @when_invoked = nil
+ end
+
+ ##
+ # True if +long+ begins with the characters from +short+.
+
+ def begins?(long, short)
+ return false if short.nil?
+ long[0, short.length] == short
+ end
+
+ ##
+ # Override to provide command handling.
+ #
+ # #options will be filled in with your parsed options, unparsed options will
+ # be left in <tt>options[:args]</tt>.
+ #
+ # See also: #get_all_gem_names, #get_one_gem_name,
+ # #get_one_optional_argument
+
+ def execute
+ raise Gem::Exception, "generic command has no actions"
+ end
+
+ ##
+ # Display to the user that a gem couldn't be found and reasons why
+ #--
+ # TODO: replace +domain+ with a parameter to suppress suggestions
+
+ def show_lookup_failure(gem_name, version, errors, domain)
+ if errors and !errors.empty?
+ msg = "Could not find a valid gem '#{gem_name}' (#{version}), here is why:\n"
+ errors.each { |x| msg << " #{x.wordy}\n" }
+ alert_error msg
+ else
+ alert_error "Could not find a valid gem '#{gem_name}' (#{version}) in any repository"
+ end
+
+ unless domain == :local then # HACK
+ suggestions = Gem::SpecFetcher.fetcher.suggest_gems_from_name gem_name
+
+ unless suggestions.empty?
+ alert_error "Possible alternatives: #{suggestions.join(", ")}"
+ end
+ end
+ end
+
+ ##
+ # Get all gem names from the command line.
+
+ def get_all_gem_names
+ args = options[:args]
+
+ if args.nil? or args.empty? then
+ raise Gem::CommandLineError,
+ "Please specify at least one gem name (e.g. gem build GEMNAME)"
+ end
+
+ args.select { |arg| arg !~ /^-/ }
+ 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 do |name|
+ if /\A(.*):(#{Gem::Requirement::PATTERN_RAW})\z/ =~ name
+ [$1, $2]
+ else
+ [name]
+ end
+ end
+ end
+
+ ##
+ # 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.
+
+ def get_one_gem_name
+ args = options[:args]
+
+ if args.nil? or args.empty? then
+ raise Gem::CommandLineError,
+ "Please specify a gem name on the command line (e.g. gem build GEMNAME)"
+ end
+
+ if args.size > 1 then
+ raise Gem::CommandLineError,
+ "Too many gem names (#{args.join(', ')}); please specify only one"
+ end
+
+ args.first
+ end
+
+ ##
+ # Get a single optional argument from the command line. If more than one
+ # argument is given, return only the first. Return nil if none are given.
+
+ def get_one_optional_argument
+ args = options[:args] || []
+ args.first
+ end
+
+ ##
+ # Override to provide details of the arguments a command takes. It should
+ # return a left-justified string, one argument per line.
+ #
+ # For example:
+ #
+ # def usage
+ # "#{program_name} FILE [FILE ...]"
+ # end
+ #
+ # def arguments
+ # "FILE name of file to find"
+ # end
+
+ def arguments
+ ""
+ end
+
+ ##
+ # Override to display the default values of the command options. (similar to
+ # +arguments+, but displays the default values).
+ #
+ # For example:
+ #
+ # def defaults_str
+ # --no-gems-first --no-all
+ # end
+
+ def defaults_str
+ ""
+ end
+
+ ##
+ # Override to display a longer description of what this command does.
+
+ def description
+ nil
+ end
+
+ ##
+ # Override to display the usage for an individual gem command.
+ #
+ # The text "[options]" is automatically appended to the usage text.
+
+ def usage
+ program_name
+ end
+
+ ##
+ # Display the help message for the command.
+
+ def show_help
+ parser.program_name = usage
+ say parser
+ end
+
+ ##
+ # Invoke the command with the given list of arguments.
+
+ 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
+
+ options[:build_args] = build_args
+
+ if options[:help] then
+ show_help
+ elsif @when_invoked then
+ @when_invoked.call options
+ else
+ execute
+ end
+ end
+
+ ##
+ # Call the given block when invoked.
+ #
+ # Normal command invocations just executes the +execute+ method of the
+ # command. Specifying an invocation block allows the test methods to
+ # override the normal action of a command to determine that it has been
+ # invoked correctly.
+
+ def when_invoked(&block)
+ @when_invoked = block
+ end
+
+ ##
+ # Add a command-line option and handler to the command.
+ #
+ # See OptionParser#make_switch for an explanation of +opts+.
+ #
+ # +handler+ will be called with two values, the value of the argument and
+ # the options hash.
+ #
+ # If the first argument of add_option is a Symbol, it's used to group
+ # options in output. See `gem help list` for an example.
+
+ def add_option(*opts, &handler) # :yields: value, options
+ group_name = Symbol === opts.first ? opts.shift : :options
+
+ @option_groups[group_name] << [opts, handler]
+ end
+
+ ##
+ # Remove previously defined command-line argument +name+.
+
+ def remove_option(name)
+ @option_groups.each do |_, option_list|
+ option_list.reject! { |args, _| args.any? { |x| x =~ /^#{name}/ } }
+ end
+ end
+
+ ##
+ # Merge a set of command options with the set of default options (without
+ # modifying the default option hash).
+
+ def merge_options(new_options)
+ @options = @defaults.clone
+ new_options.each do |k,v| @options[k] = v end
+ end
+
+ ##
+ # True if the command handles the given argument list.
+
+ def handles?(args)
+ begin
+ parser.parse!(args.dup)
+ return true
+ rescue
+ return false
+ end
+ end
+
+ ##
+ # Handle the given list of arguments by parsing them and recording the
+ # results.
+
+ def handle_options(args)
+ args = add_extra_args(args)
+ @options = Marshal.load Marshal.dump @defaults # deep copy
+ parser.parse!(args)
+ @options[:args] = args
+ end
+
+ ##
+ # Adds extra args from ~/.gemrc
+
+ def add_extra_args(args)
+ result = []
+
+ s_extra = Gem::Command.specific_extra_args(@command)
+ extra = Gem::Command.extra_args + s_extra
+
+ until extra.empty? do
+ ex = []
+ ex << extra.shift
+ ex << extra.shift if extra.first.to_s =~ /^[^-]/
+ result << ex if handles?(ex)
+ end
+
+ result.flatten!
+ result.concat(args)
+ result
+ end
+
+ private
+
+ def add_parser_description # :nodoc:
+ return unless description
+
+ 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
+
+ def add_parser_options # :nodoc:
+ @parser.separator nil
+
+ regular_options = @option_groups.delete :options
+
+ configure_options "", regular_options
+
+ @option_groups.sort_by { |n,_| n.to_s }.each do |group_name, option_list|
+ @parser.separator nil
+ configure_options group_name, option_list
+ 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
+ configure_options "Common", Gem::Command.common_options
+
+ add_parser_run_info "Arguments", arguments
+ add_parser_summary
+ add_parser_description
+ add_parser_run_info "Defaults", defaults_str
+ end
+
+ def configure_options(header, option_list)
+ return if option_list.nil? or option_list.empty?
+
+ header = header.to_s.empty? ? '' : "#{header} "
+ @parser.separator " #{header}Options:"
+
+ option_list.each do |args, handler|
+ args.select { |arg| arg =~ /^-/ }
+ @parser.on(*args) do |value|
+ handler.call(value, @options)
+ end
+ end
+
+ @parser.separator ''
+ end
+
+ ##
+ # Wraps +text+ to +width+
+
+ def wrap(text, width) # :doc:
+ text.gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
+ end
+
+ # ----------------------------------------------------------------
+ # Add the options common to all commands.
+
+ add_common_option('-h', '--help',
+ 'Get help on this command') do |value, options|
+ options[:help] = true
+ end
+
+ add_common_option('-V', '--[no-]verbose',
+ 'Set the verbose level of output') do |value, options|
+ # Set us to "really verbose" so the progress meter works
+ if Gem.configuration.verbose and value then
+ Gem.configuration.verbose = 1
+ else
+ Gem.configuration.verbose = value
+ end
+ end
+
+ add_common_option('-q', '--quiet', 'Silence commands') do |value, options|
+ Gem.configuration.verbose = false
+ end
+
+ # Backtrace and config-file are added so they show up in the help
+ # commands. Both options are actually handled before the other
+ # options get parsed.
+
+ add_common_option('--config-file FILE',
+ 'Use this config file instead of default') do
+ end
+
+ add_common_option('--backtrace',
+ 'Show stack backtrace on errors') do
+ end
+
+ add_common_option('--debug',
+ 'Turn on Ruby debugging') do
+ end
+
+ # :stopdoc:
+
+ HELP = <<-HELP
+RubyGems is a sophisticated package manager for Ruby. This is a
+basic help message containing pointers to more information.
+
+ Usage:
+ gem -h/--help
+ gem -v/--version
+ gem command [arguments...] [options...]
+
+ Examples:
+ gem install rake
+ gem list --local
+ gem build package.gemspec
+ gem help install
+
+ Further help:
+ gem help commands list all 'gem' commands
+ gem help examples show some examples of usage
+ gem help gem_dependencies gem dependencies file guide
+ gem help platforms gem platforms guide
+ gem help <COMMAND> show help on COMMAND
+ (e.g. 'gem help install')
+ gem server present a web page at
+ http://localhost:8808/
+ with info about installed gems
+ Further information:
+ http://guides.rubygems.org
+ HELP
+
+ # :startdoc:
+
+end
+
+##
+# \Commands will be placed in this namespace
+
+module Gem::Commands
+end
+
diff --git a/jni/ruby/lib/rubygems/command_manager.rb b/jni/ruby/lib/rubygems/command_manager.rb
new file mode 100644
index 0000000..53d18c2
--- /dev/null
+++ b/jni/ruby/lib/rubygems/command_manager.rb
@@ -0,0 +1,218 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/command'
+require 'rubygems/user_interaction'
+
+##
+# The command manager registers and installs all the individual sub-commands
+# supported by the gem command.
+#
+# Extra commands can be provided by writing a rubygems_plugin.rb
+# file in an installed gem. You should register your command against the
+# Gem::CommandManager instance, like this:
+#
+# # file rubygems_plugin.rb
+# 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
+# # ...
+# end
+#
+# See Gem::Command for instructions on writing gem commands.
+
+class Gem::CommandManager
+
+ include Gem::UserInteraction
+
+ BUILTIN_COMMANDS = [ # :nodoc:
+ :build,
+ :cert,
+ :check,
+ :cleanup,
+ :contents,
+ :dependency,
+ :environment,
+ :fetch,
+ :generate_index,
+ :help,
+ :install,
+ :list,
+ :lock,
+ :mirror,
+ :open,
+ :outdated,
+ :owner,
+ :pristine,
+ :push,
+ :query,
+ :rdoc,
+ :search,
+ :server,
+ :sources,
+ :specification,
+ :stale,
+ :uninstall,
+ :unpack,
+ :update,
+ :which,
+ :yank,
+ ]
+
+ ##
+ # Return the authoritative instance of the command manager.
+
+ def self.instance
+ @command_manager ||= new
+ 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.
+
+ def self.reset
+ @command_manager = nil
+ end
+
+ ##
+ # Register all the subcommands supported by the gem command.
+
+ def initialize
+ require 'timeout'
+ @commands = {}
+
+ BUILTIN_COMMANDS.each do |name|
+ register_command name
+ end
+ end
+
+ ##
+ # Register the Symbol +command+ as a gem command.
+
+ def register_command(command, obj=false)
+ @commands[command] = obj
+ end
+
+ ##
+ # Unregister the Symbol +command+ as a gem command.
+
+ def unregister_command(command)
+ @commands.delete command
+ end
+
+ ##
+ # Returns a Command instance for +command_name+
+
+ def [](command_name)
+ command_name = command_name.intern
+ return nil if @commands[command_name].nil?
+ @commands[command_name] ||= load_and_instantiate(command_name)
+ end
+
+ ##
+ # Return a sorted list of all command names as strings.
+
+ def command_names
+ @commands.keys.collect {|key| key.to_s}.sort
+ end
+
+ ##
+ # Run the command specified by +args+.
+
+ def run(args, build_args=nil)
+ process_args(args, build_args)
+ rescue StandardError, Timeout::Error => ex
+ alert_error "While executing gem ... (#{ex.class})\n #{ex}"
+ ui.backtrace ex
+
+ terminate_interaction(1)
+ rescue Interrupt
+ alert_error "Interrupted"
+ terminate_interaction(1)
+ end
+
+ def process_args(args, build_args=nil)
+ if args.empty? then
+ say Gem::Command::HELP
+ terminate_interaction 1
+ end
+
+ case args.first
+ when '-h', '--help' then
+ say Gem::Command::HELP
+ terminate_interaction 0
+ when '-v', '--version' then
+ say Gem::VERSION
+ terminate_interaction 0
+ when /^-/ then
+ alert_error "Invalid option: #{args.first}. See 'gem --help'."
+ terminate_interaction 1
+ else
+ cmd_name = args.shift.downcase
+ cmd = find_command cmd_name
+ cmd.invoke_with_build_args args, build_args
+ end
+ end
+
+ def find_command(cmd_name)
+ possibilities = find_command_possibilities cmd_name
+
+ if possibilities.size > 1 then
+ raise Gem::CommandLineError,
+ "Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]"
+ elsif possibilities.empty? then
+ raise Gem::CommandLineError, "Unknown command #{cmd_name}"
+ end
+
+ self[possibilities.first]
+ end
+
+ def find_command_possibilities(cmd_name)
+ len = cmd_name.length
+
+ found = command_names.select { |name| cmd_name == name[0, len] }
+
+ exact = found.find { |name| name == cmd_name }
+
+ exact ? [exact] : found
+ end
+
+ private
+
+ def load_and_instantiate(command_name)
+ command_name = command_name.to_s
+ const_name = command_name.capitalize.gsub(/_(.)/) { $1.upcase } << "Command"
+ load_error = nil
+
+ begin
+ begin
+ require "rubygems/commands/#{command_name}_command"
+ rescue LoadError => e
+ load_error = e
+ end
+ 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
+
diff --git a/jni/ruby/lib/rubygems/commands/build_command.rb b/jni/ruby/lib/rubygems/commands/build_command.rb
new file mode 100644
index 0000000..d975429
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/build_command.rb
@@ -0,0 +1,60 @@
+require 'rubygems/command'
+require 'rubygems/package'
+
+class Gem::Commands::BuildCommand < Gem::Command
+
+ def initialize
+ super 'build', 'Build a gem from a gemspec'
+
+ add_option '--force', 'skip validation of the spec' do |value, options|
+ options[:force] = true
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMSPEC_FILE gemspec file name to build a gem for"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The build command allows you to create a gem from a ruby gemspec.
+
+The best way to build a gem is to use a Rakefile and the Gem::PackageTask
+which ships with RubyGems.
+
+The gemspec can either be created by hand or extracted from an existing gem
+with gem spec:
+
+ $ gem unpack my_gem-1.0.gem
+ Unpacked gem: '.../my_gem-1.0'
+ $ gem spec my_gem-1.0.gem --ruby > my_gem-1.0/my_gem-1.0.gemspec
+ $ cd my_gem-1.0
+ [edit gem contents]
+ $ gem build my_gem-1.0.gemspec
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMSPEC_FILE"
+ end
+
+ def execute
+ gemspec = get_one_gem_name
+
+ if File.exist? gemspec then
+ spec = Gem::Specification.load gemspec
+
+ if spec then
+ Gem::Package.build spec, options[:force]
+ else
+ alert_error "Error loading gemspec. Aborting."
+ terminate_interaction 1
+ end
+ else
+ alert_error "Gemspec file not found: #{gemspec}"
+ terminate_interaction 1
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/cert_command.rb b/jni/ruby/lib/rubygems/commands/cert_command.rb
new file mode 100644
index 0000000..a920e7f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/cert_command.rb
@@ -0,0 +1,276 @@
+require 'rubygems/command'
+require 'rubygems/security'
+begin
+ require 'openssl'
+rescue LoadError => e
+ raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
+ e.message =~ / -- openssl$/
+end
+
+class Gem::Commands::CertCommand < Gem::Command
+
+ def initialize
+ super 'cert', 'Manage RubyGems certificates and signing settings',
+ :add => [], :remove => [], :list => [], :build => [], :sign => []
+
+ OptionParser.accept OpenSSL::X509::Certificate do |certificate|
+ begin
+ OpenSSL::X509::Certificate.new File.read certificate
+ rescue Errno::ENOENT
+ raise OptionParser::InvalidArgument, "#{certificate}: does not exist"
+ rescue OpenSSL::X509::CertificateError
+ raise OptionParser::InvalidArgument,
+ "#{certificate}: invalid X509 certificate"
+ end
+ end
+
+ OptionParser.accept OpenSSL::PKey::RSA do |key_file|
+ begin
+ passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ key = OpenSSL::PKey::RSA.new File.read(key_file), passphrase
+ rescue Errno::ENOENT
+ raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
+ rescue OpenSSL::PKey::RSAError
+ raise OptionParser::InvalidArgument, "#{key_file}: invalid RSA key"
+ 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
+
+ add_option('-b', '--build EMAIL_ADDR',
+ 'Build private key and self-signed',
+ 'certificate for EMAIL_ADDR') do |email_address, options|
+ options[:build] << email_address
+ end
+
+ add_option('-C', '--certificate CERT', OpenSSL::X509::Certificate,
+ 'Signing certificate for --sign') do |cert, options|
+ options[:issuer_cert] = cert
+ end
+
+ add_option('-K', '--private-key KEY', OpenSSL::PKey::RSA,
+ 'Key for --sign or --build') do |key, options|
+ options[:key] = key
+ end
+
+ add_option('-s', '--sign CERT',
+ 'Signs CERT with the key from -K',
+ 'and the certificate from -C') do |cert_file, options|
+ raise OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless
+ File.file? cert_file
+
+ options[:sign] << cert_file
+ end
+ end
+
+ def add_certificate certificate # :nodoc:
+ Gem::Security.trust_dir.trust_cert certificate
+
+ say "Added '#{certificate.subject}'"
+ end
+
+ def execute
+ options[:add].each do |certificate|
+ add_certificate certificate
+ end
+
+ options[:remove].each do |filter|
+ remove_certificates_matching filter
+ end
+
+ options[:list].each do |filter|
+ list_certificates_matching filter
+ end
+
+ options[:build].each do |name|
+ build name
+ end
+
+ sign_certificates unless options[:sign].empty?
+ end
+
+ def build name
+ key, key_path = build_key
+ cert_path = build_cert name, key
+
+ say "Certificate: #{cert_path}"
+
+ if key_path
+ say "Private Key: #{key_path}"
+ say "Don't forget to move the key file to somewhere private!"
+ end
+ end
+
+ def build_cert name, key # :nodoc:
+ cert = Gem::Security.create_cert_email name, key
+ Gem::Security.write cert, "gem-public_cert.pem"
+ end
+
+ def build_key # :nodoc:
+ return options[:key] if options[:key]
+
+ passphrase = ask_for_password 'Passphrase for your Private Key:'
+ say "\n"
+
+ passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
+ say "\n"
+
+ raise Gem::CommandLineError,
+ "Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation
+
+ key = Gem::Security.create_key
+ key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase
+
+ return key, key_path
+ 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 list_certificates_matching filter # :nodoc:
+ certificates_matching filter do |certificate, _|
+ # this could probably be formatted more gracefully
+ say certificate.subject.to_s
+ end
+ end
+
+ def load_default_cert
+ cert_file = File.join Gem.default_cert_path
+ 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.default_key_path
+ key = File.read key_file
+ passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ options[:key] = OpenSSL::PKey::RSA.new key, passphrase
+ 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 load_defaults # :nodoc:
+ load_default_cert unless options[:issuer_cert]
+ load_default_key unless options[:key]
+ end
+
+ def remove_certificates_matching filter # :nodoc:
+ certificates_matching filter do |certificate, path|
+ FileUtils.rm path
+ say "Removed '#{certificate.subject}'"
+ end
+ 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
+
+ def sign_certificates # :nodoc:
+ load_defaults unless options[:sign].empty?
+
+ options[:sign].each do |cert_file|
+ sign cert_file
+ end
+ end
+
+end if defined?(OpenSSL::SSL)
+
diff --git a/jni/ruby/lib/rubygems/commands/check_command.rb b/jni/ruby/lib/rubygems/commands/check_command.rb
new file mode 100644
index 0000000..8893b9c
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/check_command.rb
@@ -0,0 +1,93 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/validator'
+require 'rubygems/doctor'
+
+class Gem::Commands::CheckCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'check', 'Check a gem repository for added or missing files',
+ :alien => true, :doctor => false, :dry_run => false, :gems => true
+
+ add_option('-a', '--[no-]alien',
+ 'Report "unmanaged" or rogue files in the',
+ 'gem repository') do |value, options|
+ options[:alien] = value
+ end
+
+ add_option('--[no-]doctor',
+ 'Clean up uninstalled gems and broken',
+ 'specifications') do |value, options|
+ options[:doctor] = value
+ end
+
+ add_option('--[no-]dry-run',
+ 'Do not remove files, only report what',
+ 'would be removed') do |value, options|
+ options[:dry_run] = value
+ end
+
+ add_option('--[no-]gems',
+ 'Check installed gems for problems') do |value, options|
+ options[:gems] = value
+ end
+
+ add_version_option 'check'
+ end
+
+ def check_gems
+ say 'Checking gems...'
+ say
+ 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
+
+ def doctor
+ say 'Checking for files from uninstalled gems...'
+ say
+
+ Gem.path.each do |gem_repo|
+ doctor = Gem::Doctor.new gem_repo, options[:dry_run]
+ doctor.doctor
+ end
+ end
+
+ def execute
+ check_gems if options[:gems]
+ doctor if options[:doctor]
+ end
+
+ def arguments # :nodoc:
+ 'GEMNAME name of gem to check'
+ end
+
+ def defaults_str # :nodoc:
+ '--gems --alien'
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The check command can list and repair problems with installed gems and
+specifications and will clean up gems that have been partially uninstalled.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [OPTIONS] [GEMNAME ...]"
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/commands/cleanup_command.rb b/jni/ruby/lib/rubygems/commands/cleanup_command.rb
new file mode 100644
index 0000000..6997564
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/cleanup_command.rb
@@ -0,0 +1,165 @@
+require 'rubygems/command'
+require 'rubygems/dependency_list'
+require 'rubygems/uninstaller'
+
+class Gem::Commands::CleanupCommand < Gem::Command
+
+ def initialize
+ super 'cleanup',
+ 'Clean up old versions of installed gems',
+ :force => false, :install_dir => Gem.dir
+
+ add_option('-n', '-d', '--dryrun',
+ 'Do not uninstall gems') do |value, options|
+ options[:dryrun] = true
+ end
+
+ @candidate_gems = nil
+ @default_gems = []
+ @full = nil
+ @gems_to_cleanup = nil
+ @original_home = nil
+ @original_path = nil
+ @primary_gems = nil
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to cleanup"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-dryrun"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The cleanup command removes old versions of gems from GEM_HOME that are not
+required to meet a dependency. If a gem is installed elsewhere in GEM_PATH
+the cleanup command won't delete it.
+
+If no gems are named all gems in GEM_HOME are cleaned.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [GEMNAME ...]"
+ end
+
+ def execute
+ say "Cleaning up installed gems..."
+
+ if options[:args].empty? then
+ done = false
+ last_set = nil
+
+ until done do
+ clean_gems
+
+ this_set = @gems_to_cleanup.map { |spec| spec.full_name }.sort
+
+ done = this_set.empty? || last_set == this_set
+
+ last_set = this_set
+ end
+ else
+ clean_gems
+ end
+
+ say "Clean Up Complete"
+
+ verbose do
+ skipped = @default_gems.map { |spec| spec.full_name }
+
+ "Skipped default gems: #{skipped.join ', '}"
+ end
+ end
+
+ def clean_gems
+ get_primary_gems
+ get_candidate_gems
+ get_gems_to_cleanup
+
+ @full = Gem::DependencyList.from_specs
+
+ deplist = Gem::DependencyList.new
+ @gems_to_cleanup.each do |spec| deplist.add spec end
+
+ deps = deplist.strongly_connected_components.flatten
+
+ @original_home = Gem.dir
+ @original_path = Gem.path
+
+ deps.reverse_each do |spec|
+ uninstall_dep spec
+ end
+
+ Gem::Specification.reset
+ end
+
+ def get_candidate_gems
+ @candidate_gems = unless options[:args].empty? then
+ options[:args].map do |gem_name|
+ Gem::Specification.find_all_by_name gem_name
+ end.flatten
+ else
+ Gem::Specification.to_a
+ end
+ end
+
+ def get_gems_to_cleanup
+ gems_to_cleanup = @candidate_gems.select { |spec|
+ @primary_gems[spec.name].version != spec.version
+ }
+
+ default_gems, gems_to_cleanup = gems_to_cleanup.partition { |spec|
+ spec.default_gem?
+ }
+
+ @default_gems += default_gems
+ @default_gems.uniq!
+ @gems_to_cleanup = gems_to_cleanup.uniq
+ end
+
+ def get_primary_gems
+ @primary_gems = {}
+
+ Gem::Specification.each do |spec|
+ if @primary_gems[spec.name].nil? or
+ @primary_gems[spec.name].version < spec.version then
+ @primary_gems[spec.name] = spec
+ end
+ end
+ end
+
+ def uninstall_dep spec
+ return unless @full.ok_to_remove?(spec.full_name)
+
+ if options[:dryrun] then
+ say "Dry Run Mode: Would uninstall #{spec.full_name}"
+ return
+ end
+
+ say "Attempting to uninstall #{spec.full_name}"
+
+ uninstall_options = {
+ :executables => false,
+ :version => "= #{spec.version}",
+ }
+
+ uninstall_options[:user_install] = Gem.user_dir == spec.base_dir
+
+ uninstaller = Gem::Uninstaller.new spec.name, uninstall_options
+
+ begin
+ uninstaller.uninstall
+ rescue Gem::DependencyRemovalException, Gem::InstallError,
+ Gem::GemNotInHomeException, Gem::FilePermissionError => e
+ say "Unable to uninstall #{spec.full_name}:"
+ say "\t#{e.class}: #{e.message}"
+ end
+ ensure
+ # Restore path Gem::Uninstaller may have changed
+ Gem.use_paths @original_home, *@original_path
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/commands/contents_command.rb b/jni/ruby/lib/rubygems/commands/contents_command.rb
new file mode 100644
index 0000000..4b944f1
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/contents_command.rb
@@ -0,0 +1,190 @@
+require 'English'
+require 'rubygems/command'
+require 'rubygems/version_option'
+
+class Gem::Commands::ContentsCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'contents', 'Display the contents of the installed gems',
+ :specdirs => [], :lib_only => false, :prefix => true,
+ :show_install_dir => false
+
+ add_version_option
+
+ add_option( '--all',
+ "Contents for all gems") do |all, options|
+ options[:all] = all
+ end
+
+ add_option('-s', '--spec-dir a,b,c', Array,
+ "Search for gems under specific paths") do |spec_dirs, options|
+ options[:specdirs] = spec_dirs
+ end
+
+ add_option('-l', '--[no-]lib-only',
+ "Only return files in the Gem's lib_dirs") do |lib_only, options|
+ options[:lib_only] = lib_only
+ end
+
+ add_option( '--[no-]prefix',
+ "Don't include installed path prefix") do |prefix, options|
+ options[:prefix] = prefix
+ end
+
+ add_option( '--[no-]show-install-dir',
+ 'Show only the gem install dir') do |show, options|
+ options[:show_install_dir] = show
+ end
+
+ @path_kind = nil
+ @spec_dirs = nil
+ @version = nil
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to list contents for"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-lib-only --prefix"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The contents command lists the files in an installed gem. The listing can
+be given as full file names, file names without the installed directory
+prefix or only the files that are requireable.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...]"
+ end
+
+ def execute
+ @version = options[:version] || Gem::Requirement.default
+ @spec_dirs = specification_directories
+ @path_kind = path_description @spec_dirs
+
+ names = gem_names
+
+ names.each do |name|
+ found =
+ if options[:show_install_dir] then
+ gem_install_dir name
+ else
+ gem_contents name
+ end
+
+ terminate_interaction 1 unless found or names.length > 1
+ end
+ end
+
+ def files_in spec
+ if spec.default_gem? then
+ files_in_default_gem spec
+ else
+ files_in_gem spec
+ end
+ end
+
+ def files_in_gem spec
+ 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)}\//
+
+ Dir[glob].map do |file|
+ [gem_path, file.sub(prefix_re, "")]
+ end
+ end
+
+ def files_in_default_gem spec
+ spec.files.map do |file|
+ case file
+ when /\A#{spec.bindir}\//
+ [RbConfig::CONFIG['bindir'], $POSTMATCH]
+ when /\.so\z/
+ [RbConfig::CONFIG['archdir'], file]
+ else
+ [RbConfig::CONFIG['rubylibdir'], file]
+ end
+ end
+ end
+
+ def gem_contents name
+ spec = spec_for name
+
+ return false unless spec
+
+ files = files_in spec
+
+ show_files files
+
+ true
+ end
+
+ def gem_install_dir name
+ spec = spec_for name
+
+ return false unless spec
+
+ say spec.gem_dir
+
+ true
+ end
+
+ def gem_names # :nodoc:
+ if options[:all] then
+ Gem::Specification.map(&:name)
+ else
+ get_all_gem_names
+ end
+ end
+
+ def path_description spec_dirs # :nodoc:
+ if spec_dirs.empty? then
+ "default gem paths"
+ else
+ "specified path"
+ end
+ end
+
+ def show_files files
+ files.sort.each do |prefix, basename|
+ absolute_path = File.join(prefix, basename)
+ next if File.directory? absolute_path
+
+ if options[:prefix] then
+ say absolute_path
+ else
+ say basename
+ end
+ end
+ end
+
+ def spec_for name
+ spec = Gem::Specification.find_all_by_name(name, @version).last
+
+ return spec if spec
+
+ say "Unable to find gem '#{name}' in #{@path_kind}"
+
+ if Gem.configuration.verbose then
+ say "\nDirectories searched:"
+ @spec_dirs.sort.each { |dir| say dir }
+ end
+
+ return nil
+ end
+
+ def specification_directories # :nodoc:
+ options[:specdirs].map do |i|
+ [i, File.join(i, "specifications")]
+ end.flatten
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/dependency_command.rb b/jni/ruby/lib/rubygems/commands/dependency_command.rb
new file mode 100644
index 0000000..4a54a3e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/dependency_command.rb
@@ -0,0 +1,207 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/version_option'
+
+class Gem::Commands::DependencyCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'dependency',
+ 'Show the dependencies of an installed gem',
+ :version => Gem::Requirement.default, :domain => :local
+
+ add_version_option
+ add_platform_option
+ add_prerelease_option
+
+ add_option('-R', '--[no-]reverse-dependencies',
+ 'Include reverse dependencies in the output') do
+ |value, options|
+ options[:reverse_dependencies] = value
+ end
+
+ add_option('-p', '--pipe',
+ "Pipe Format (name --version ver)") do |value, options|
+ options[:pipe_format] = value
+ end
+
+ add_local_remote_options
+ end
+
+ def arguments # :nodoc:
+ "REGEXP show dependencies for gems whose names start with REGEXP"
+ end
+
+ def defaults_str # :nodoc:
+ "--local --version '#{Gem::Requirement.default}' --no-reverse-dependencies"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The dependency commands lists which other gems a given gem depends on. For
+local gems only the reverse dependencies can be shown (which gems depend on
+the named gem).
+
+The dependency list can be displayed in a format suitable for piping for
+use with other commands.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} REGEXP"
+ end
+
+ def fetch_remote_specs dependency # :nodoc:
+ fetcher = Gem::SpecFetcher.fetcher
+
+ ss, = fetcher.spec_for_dependency dependency
+
+ ss.map { |spec, _| spec }
+ end
+
+ def fetch_specs dependency # :nodoc:
+ specs = []
+
+ specs.concat dependency.matching_specs if local?
+ specs.concat fetch_remote_specs dependency if remote?
+
+ ensure_specs specs
+
+ specs.uniq.sort
+ end
+
+ def gem_dependency args, version, prerelease # :nodoc:
+ args << '' if args.empty?
+
+ pattern = if args.length == 1 and args.first =~ /\A\/(.*)\/(i)?\z/m then
+ flags = $2 ? Regexp::IGNORECASE : nil
+ Regexp.new $1, flags
+ else
+ /\A#{Regexp.union(*args)}/
+ end
+
+ dependency = Gem::Deprecate.skip_during {
+ Gem::Dependency.new pattern, version
+ }
+
+ dependency.prerelease = prerelease
+
+ dependency
+ end
+
+ def display_pipe specs # :nodoc:
+ specs.each do |spec|
+ unless spec.dependencies.empty? then
+ spec.dependencies.sort_by { |dep| dep.name }.each do |dep|
+ say "#{dep.name} --version '#{dep.requirement}'"
+ end
+ end
+ end
+ end
+
+ def display_readable specs, reverse # :nodoc:
+ response = ''
+
+ specs.each do |spec|
+ response << print_dependencies(spec)
+ unless reverse[spec.full_name].empty? then
+ response << " Used by\n"
+ reverse[spec.full_name].each do |sp, dep|
+ response << " #{sp} (#{dep})\n"
+ end
+ end
+ response << "\n"
+ end
+
+ say response
+ end
+
+ def execute
+ ensure_local_only_reverse_dependencies
+
+ dependency =
+ gem_dependency options[:args], options[:version], options[:prerelease]
+
+ specs = fetch_specs dependency
+
+ reverse = reverse_dependencies specs
+
+ if options[:pipe_format] then
+ display_pipe specs
+ else
+ display_readable specs, reverse
+ end
+ end
+
+ def ensure_local_only_reverse_dependencies # :nodoc:
+ if options[:reverse_dependencies] and remote? and not local? then
+ alert_error 'Only reverse dependencies for local gems are supported.'
+ terminate_interaction 1
+ end
+ end
+
+ def ensure_specs specs # :nodoc:
+ return unless specs.empty?
+
+ patterns = options[:args].join ','
+ say "No gems found matching #{patterns} (#{options[:version]})" if
+ Gem.configuration.verbose
+
+ terminate_interaction 1
+ end
+
+ def print_dependencies(spec, level = 0) # :nodoc:
+ response = ''
+ response << ' ' * level + "Gem #{spec.full_name}\n"
+ unless spec.dependencies.empty? then
+ spec.dependencies.sort_by { |dep| dep.name }.each do |dep|
+ response << ' ' * level + " #{dep}\n"
+ end
+ end
+ response
+ end
+
+ def remote_specs dependency # :nodoc:
+ fetcher = Gem::SpecFetcher.fetcher
+
+ ss, _ = fetcher.spec_for_dependency dependency
+
+ ss.map { |s,o| s }
+ end
+
+ def reverse_dependencies specs # :nodoc:
+ reverse = Hash.new { |h, k| h[k] = [] }
+
+ return reverse unless options[:reverse_dependencies]
+
+ specs.each do |spec|
+ reverse[spec.full_name] = find_reverse_dependencies spec
+ end
+
+ reverse
+ end
+
+ ##
+ # Returns an Array of [specification, dep] that are satisfied by +spec+.
+
+ def find_reverse_dependencies spec # :nodoc:
+ result = []
+
+ Gem::Specification.each do |sp|
+ sp.dependencies.each do |dep|
+ dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
+
+ if spec.name == dep.name and
+ dep.requirement.satisfied_by?(spec.version) then
+ result << [sp.full_name, dep]
+ end
+ end
+ end
+
+ result
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/environment_command.rb b/jni/ruby/lib/rubygems/commands/environment_command.rb
new file mode 100644
index 0000000..067d0b1
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/environment_command.rb
@@ -0,0 +1,158 @@
+require 'rubygems/command'
+
+class Gem::Commands::EnvironmentCommand < Gem::Command
+
+ def initialize
+ super 'environment', 'Display information about the RubyGems environment'
+ end
+
+ def arguments # :nodoc:
+ args = <<-EOF
+ packageversion display the package version
+ gemdir display the path where gems are installed
+ gempath display path used to search for gems
+ version display the gem format version
+ remotesources display the remote gem servers
+ platform display the supported gem platforms
+ <omitted> display everything
+ EOF
+ return args.gsub(/^\s+/, '')
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The environment command lets you query rubygems for its configuration for
+use in shell scripts or as a debugging aid.
+
+The RubyGems environment can be controlled through command line arguments,
+gemrc files, environment variables and built-in defaults.
+
+Command line argument defaults and some RubyGems defaults can be set in a
+~/.gemrc file for individual users and a gemrc in the SYSTEM CONFIGURATION
+DIRECTORY for all users. These files are YAML files with the following YAML
+keys:
+
+ :sources: A YAML array of remote gem repositories to install gems from
+ :verbose: Verbosity of the gem command. false, true, and :really are the
+ levels
+ :update_sources: Enable/disable automatic updating of repository metadata
+ :backtrace: Print backtrace when RubyGems encounters an error
+ :gempath: The paths in which to look for gems
+ :disable_default_gem_server: Force specification of gem server host on push
+ <gem_command>: A string containing arguments for the specified gem command
+
+Example:
+
+ :verbose: false
+ install: --no-wrappers
+ update: --no-wrappers
+ :disable_default_gem_server: true
+
+RubyGems' default local repository can be overridden with the GEM_PATH and
+GEM_HOME environment variables. GEM_HOME sets the default repository to
+install into. GEM_PATH allows multiple local repositories to be searched for
+gems.
+
+If you are behind a proxy server, RubyGems uses the HTTP_PROXY,
+HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the
+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
+lib/rubygems/defaults.rb. You may override these in
+lib/rubygems/defaults/operating_system.rb
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [arg]"
+ end
+
+ def execute
+ out = ''
+ arg = options[:args][0]
+ out <<
+ case arg
+ when /^packageversion/ then
+ Gem::RubyGemsPackageVersion
+ when /^version/ then
+ Gem::VERSION
+ when /^gemdir/, /^gemhome/, /^home/, /^GEM_HOME/ then
+ Gem.dir
+ when /^gempath/, /^path/, /^GEM_PATH/ then
+ Gem.path.join(File::PATH_SEPARATOR)
+ when /^remotesources/ then
+ Gem.sources.to_a.join("\n")
+ when /^platform/ then
+ Gem.platforms.join(File::PATH_SEPARATOR)
+ when nil then
+ show_environment
+ else
+ raise Gem::CommandLineError, "Unknown environment option [#{arg}]"
+ end
+ say out
+ true
+ end
+
+ def add_path out, path
+ path.each do |component|
+ out << " - #{component}\n"
+ end
+ end
+
+ def show_environment # :nodoc:
+ out = "RubyGems Environment:\n"
+
+ out << " - RUBYGEMS VERSION: #{Gem::VERSION}\n"
+
+ out << " - RUBY VERSION: #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
+ out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+ out << ") [#{RUBY_PLATFORM}]\n"
+
+ out << " - INSTALLATION DIRECTORY: #{Gem.dir}\n"
+
+ out << " - RUBYGEMS PREFIX: #{Gem.prefix}\n" unless Gem.prefix.nil?
+
+ out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
+
+ out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
+
+ out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
+
+ out << " - SYSTEM CONFIGURATION DIRECTORY: #{Gem::ConfigFile::SYSTEM_CONFIG_PATH}\n"
+
+ out << " - RUBYGEMS PLATFORMS:\n"
+ Gem.platforms.each do |platform|
+ out << " - #{platform}\n"
+ end
+
+ out << " - GEM PATHS:\n"
+ out << " - #{Gem.dir}\n"
+
+ gem_path = Gem.path.dup
+ gem_path.delete Gem.dir
+ add_path out, gem_path
+
+ out << " - GEM CONFIGURATION:\n"
+ Gem.configuration.each do |name, value|
+ value = value.gsub(/./, '*') if name == 'gemcutter_key'
+ out << " - #{name.inspect} => #{value.inspect}\n"
+ end
+
+ out << " - REMOTE SOURCES:\n"
+ Gem.sources.each do |s|
+ out << " - #{s}\n"
+ end
+
+ out << " - SHELL PATH:\n"
+
+ shell_path = ENV['PATH'].split(File::PATH_SEPARATOR)
+ add_path out, shell_path
+
+ out
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/fetch_command.rb b/jni/ruby/lib/rubygems/commands/fetch_command.rb
new file mode 100644
index 0000000..c57ab00
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/fetch_command.rb
@@ -0,0 +1,77 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/version_option'
+
+class Gem::Commands::FetchCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'fetch', 'Download a gem and place it in the current directory'
+
+ add_bulk_threshold_option
+ add_proxy_option
+ add_source_option
+ add_clear_sources_option
+
+ add_version_option
+ add_platform_option
+ add_prerelease_option
+ end
+
+ def arguments # :nodoc:
+ 'GEMNAME name of gem to download'
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The fetch command fetches gem files that can be stored for later use or
+unpacked to examine their contents.
+
+See the build command help for an example of unpacking a gem, modifying it,
+then repackaging it.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...]"
+ end
+
+ def execute
+ version = options[:version] || Gem::Requirement.default
+
+ platform = Gem.platforms.last
+ gem_names = get_all_gem_names
+
+ gem_names.each do |gem_name|
+ dep = Gem::Dependency.new gem_name, version
+ dep.prerelease = options[:prerelease]
+
+ specs_and_sources, errors =
+ Gem::SpecFetcher.fetcher.spec_for_dependency dep
+
+ if platform then
+ filtered = specs_and_sources.select { |s,| s.platform == platform }
+ specs_and_sources = filtered unless filtered.empty?
+ end
+
+ spec, source = specs_and_sources.max_by { |s,| s.version }
+
+ if spec.nil? then
+ show_lookup_failure gem_name, version, errors, options[:domain]
+ next
+ end
+
+ source.download spec
+
+ say "Downloaded #{spec.full_name}"
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/generate_index_command.rb b/jni/ruby/lib/rubygems/commands/generate_index_command.rb
new file mode 100644
index 0000000..ca6f694
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/generate_index_command.rb
@@ -0,0 +1,84 @@
+require 'rubygems/command'
+require 'rubygems/indexer'
+
+##
+# Generates a index files for use as a gem server.
+#
+# See `gem help generate_index`
+
+class Gem::Commands::GenerateIndexCommand < Gem::Command
+
+ def initialize
+ super 'generate_index',
+ 'Generates the index files for a gem server directory',
+ :directory => '.', :build_modern => true
+
+ add_option '-d', '--directory=DIRNAME',
+ 'repository base dir containing gems subdir' do |dir, options|
+ options[:directory] = File.expand_path dir
+ end
+
+ add_option '--[no-]modern',
+ 'Generate indexes for RubyGems',
+ '(always true)' do |value, options|
+ options[:build_modern] = value
+ end
+
+ add_option '--update',
+ 'Update modern indexes with gems added',
+ 'since the last update' do |value, options|
+ options[:update] = value
+ end
+ end
+
+ def defaults_str # :nodoc:
+ "--directory . --modern"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The generate_index command creates a set of indexes for serving gems
+statically. The command expects a 'gems' directory under the path given to
+the --directory option. The given directory will be the directory you serve
+as the gem repository.
+
+For `gem generate_index --directory /path/to/repo`, expose /path/to/repo via
+your HTTP server configuration (not /path/to/repo/gems).
+
+When done, it will generate a set of files like this:
+
+ gems/*.gem # .gem files you want to
+ # index
+
+ specs.<version>.gz # specs index
+ latest_specs.<version>.gz # latest specs index
+ prerelease_specs.<version>.gz # prerelease specs index
+ quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file
+
+The .rz extension files are compressed with the inflate algorithm.
+The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and
+Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
+ EOF
+ end
+
+ def execute
+ # This is always true because it's the only way now.
+ options[:build_modern] = true
+
+ if not File.exist?(options[:directory]) or
+ not File.directory?(options[:directory]) then
+ alert_error "unknown directory name #{directory}."
+ terminate_interaction 1
+ else
+ indexer = Gem::Indexer.new options.delete(:directory), options
+
+ if options[:update] then
+ indexer.update_index
+ else
+ indexer.generate_index
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/help_command.rb b/jni/ruby/lib/rubygems/commands/help_command.rb
new file mode 100644
index 0000000..ed81ad6
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/help_command.rb
@@ -0,0 +1,384 @@
+require 'rubygems/command'
+
+class Gem::Commands::HelpCommand < Gem::Command
+
+ # :stopdoc:
+ EXAMPLES = <<-EOF
+Some examples of 'gem' usage.
+
+* Install 'rake', either from local directory or remote server:
+
+ gem install rake
+
+* Install 'rake', only from remote server:
+
+ gem install rake --remote
+
+* Install 'rake', but only version 0.3.1, even if dependencies
+ are not met, and into a user-specific directory:
+
+ gem install rake --version 0.3.1 --force --user-install
+
+* List local gems whose name begins with 'D':
+
+ gem list D
+
+* List local and remote gems whose name contains 'log':
+
+ gem search log --both
+
+* List only remote gems whose name contains 'log':
+
+ gem search log --remote
+
+* Uninstall 'rake':
+
+ gem uninstall rake
+
+* Create a gem:
+
+ See http://guides.rubygems.org/make-your-own-gem/
+
+* See information about RubyGems:
+
+ gem environment
+
+* Update all gems on your system:
+
+ gem update
+
+* Update your local version of RubyGems
+
+ gem update --system
+ EOF
+
+ GEM_DEPENDENCIES = <<-EOF
+A gem dependencies file allows installation of a consistent set of gems across
+multiple environments. The RubyGems implementation is designed to be
+compatible with Bundler's Gemfile format. You can see additional
+documentation on the format at:
+
+ http://bundler.io
+
+RubyGems automatically looks for these gem dependencies files:
+
+* gem.deps.rb
+* Gemfile
+* Isolate
+
+These files are looked up automatically using `gem install -g`, or you can
+specify a custom file.
+
+When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies
+file the gems from that file will be activated at startup time. Set it to a
+specific filename or to "-" to have RubyGems automatically discover the gem
+dependencies file by walking up from the current directory.
+
+You can also activate gem dependencies at program startup using
+Gem.use_gemdeps.
+
+NOTE: Enabling automatic discovery on multiuser systems can lead to execution
+of arbitrary code when used from directories outside your control.
+
+Gem Dependencies
+================
+
+Use #gem to declare which gems you directly depend upon:
+
+ gem 'rake'
+
+To depend on a specific set of versions:
+
+ gem 'rake', '~> 10.3', '>= 10.3.2'
+
+RubyGems will require the gem name when activating the gem using
+the RUBYGEMS_GEMDEPS environment variable or Gem::use_gemdeps. Use the
+require: option to override this behavior if the gem does not have a file of
+that name or you don't want to require those files:
+
+ gem 'my_gem', require: 'other_file'
+
+To prevent RubyGems from requiring any files use:
+
+ gem 'my_gem', require: false
+
+To load dependencies from a .gemspec file:
+
+ gemspec
+
+RubyGems looks for the first .gemspec file in the current directory. To
+override this use the name: option:
+
+ gemspec name: 'specific_gem'
+
+To look in a different directory use the path: option:
+
+ gemspec name: 'specific_gem', path: 'gemspecs'
+
+To depend on a gem unpacked into a local directory:
+
+ gem 'modified_gem', path: 'vendor/modified_gem'
+
+To depend on a gem from git:
+
+ gem 'private_gem', git: 'git@my.company.example:private_gem.git'
+
+To depend on a gem from github:
+
+ gem 'private_gem', github: 'my_company/private_gem'
+
+To depend on a gem from a github gist:
+
+ gem 'bang', gist: '1232884'
+
+Git, github and gist support the ref:, branch: and tag: options to specify a
+commit reference or hash, branch or tag respectively to use for the gem.
+
+Setting the submodules: option to true for git, github and gist dependencies
+causes fetching of submodules when fetching the repository.
+
+You can depend on multiple gems from a single repository with the git method:
+
+ git 'https://github.com/rails/rails.git' do
+ gem 'activesupport'
+ gem 'activerecord'
+ end
+
+Gem Sources
+===========
+
+RubyGems uses the default sources for regular `gem install` for gem
+dependencies files. Unlike bundler, you do need to specify a source.
+
+You can override the sources used for downloading gems with:
+
+ source 'https://gem_server.example'
+
+You may specify multiple sources. Unlike bundler the prepend: option is not
+supported. Sources are used in-order, to prepend a source place it at the
+front of the list.
+
+Gem Platform
+============
+
+You can restrict gem dependencies to specific platforms with the #platform
+and #platforms methods:
+
+ platform :ruby_21 do
+ gem 'debugger'
+ end
+
+See the bundler Gemfile manual page for a list of platforms supported in a gem
+dependencies file.:
+
+ http://bundler.io/v1.6/man/gemfile.5.html
+
+Ruby Version and Engine Dependency
+==================================
+
+You can specifiy the version, engine and engine version of ruby to use with
+your gem dependencies file. If you are not running the specified version
+RubyGems will raise an exception.
+
+To depend on a specific version of ruby:
+
+ ruby '2.1.2'
+
+To depend on a specific ruby engine:
+
+ ruby '1.9.3', engine: 'jruby'
+
+To depend on a specific ruby engine version:
+
+ ruby '1.9.3', engine: 'jruby', engine_version: '1.7.11'
+
+Grouping Dependencies
+=====================
+
+Gem dependencies may be placed in groups that can be excluded from install.
+Dependencies required for development or testing of your code may be excluded
+when installed in a production environment.
+
+A #gem dependency may be placed in a group using the group: option:
+
+ gem 'minitest', group: :test
+
+To install dependencies from a gemfile without specific groups use the
+`--without` option for `gem install -g`:
+
+ $ gem install -g --without test
+
+The group: option also accepts multiple groups if the gem fits in multiple
+categories.
+
+Multiple groups may be excluded during install by comma-separating the groups for `--without` or by specifying `--without` multiple times.
+
+The #group method can also be used to place gems in groups:
+
+ group :test do
+ gem 'minitest'
+ gem 'minitest-emoji'
+ end
+
+The #group method allows multiple groups.
+
+The #gemspec development dependencies are placed in the :development group by
+default. This may be overriden with the :development_group option:
+
+ gemspec development_group: :other
+
+ EOF
+
+ PLATFORMS = <<-'EOF'
+RubyGems platforms are composed of three parts, a CPU, an OS, and a
+version. These values are taken from values in rbconfig.rb. You can view
+your current platform by running `gem environment`.
+
+RubyGems matches platforms as follows:
+
+ * The CPU must match exactly unless one of the platforms has
+ "universal" as the CPU or the local CPU starts with "arm" and the gem's
+ CPU is exactly "arm" (for gems that support generic ARM architecture).
+ * The OS must match exactly.
+ * The versions must match exactly unless one of the versions is nil.
+
+For commands that install, uninstall and list gems, you can override what
+RubyGems thinks your platform is with the --platform option. The platform
+you pass must match "#{cpu}-#{os}" or "#{cpu}-#{os}-#{version}". On mswin
+platforms, the version is the compiler version, not the OS version. (Ruby
+compiled with VC6 uses "60" as the compiler version, VC8 uses "80".)
+
+For the ARM architecture, gems with a platform of "arm-linux" should run on a
+reasonable set of ARM CPUs and not depend on instructions present on a limited
+subset of the architecture. For example, the binary should run on platforms
+armv5, armv6hf, armv6l, armv7, etc. If you use the "arm-linux" platform
+please test your gem on a variety of ARM hardware before release to ensure it
+functions correctly.
+
+Example platforms:
+
+ x86-freebsd # Any FreeBSD version on an x86 CPU
+ universal-darwin-8 # Darwin 8 only gems that run on any CPU
+ x86-mswin32-80 # Windows gems compiled with VC8
+ armv7-linux # Gem complied for an ARMv7 CPU running linux
+ arm-linux # Gem compiled for any ARM CPU running linux
+
+When building platform gems, set the platform in the gem specification to
+Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
+platform.
+ EOF
+
+ # NOTE when updating also update Gem::Command::HELP
+
+ SUBCOMMANDS = [
+ ["commands", :show_commands],
+ ["options", Gem::Command::HELP],
+ ["examples", EXAMPLES],
+ ["gem_dependencies", GEM_DEPENDENCIES],
+ ["platforms", PLATFORMS],
+ ]
+ # :startdoc:
+
+ def initialize
+ super 'help', "Provide help on the 'gem' command"
+
+ @command_manager = Gem::CommandManager.instance
+ end
+
+ def usage # :nodoc:
+ "#{program_name} ARGUMENT"
+ end
+
+ def execute
+ arg = options[:args][0]
+
+ _, help = SUBCOMMANDS.find do |command,|
+ begins? command, arg
+ end
+
+ if help then
+ if Symbol === help then
+ send help
+ else
+ say help
+ end
+ return
+ end
+
+ if options[:help] then
+ show_help
+
+ elsif arg then
+ show_command_help arg
+
+ else
+ say Gem::Command::HELP
+ end
+ end
+
+ def show_commands # :nodoc:
+ out = []
+ out << "GEM commands are:"
+ out << nil
+
+ margin_width = 4
+
+ desc_width = @command_manager.command_names.map { |n| n.size }.max + 4
+
+ summary_width = 80 - margin_width - desc_width
+ wrap_indent = ' ' * (margin_width + desc_width)
+ format = "#{' ' * margin_width}%-#{desc_width}s%s"
+
+ @command_manager.command_names.each do |cmd_name|
+ command = @command_manager[cmd_name]
+
+ summary =
+ if command then
+ command.summary
+ else
+ "[No command found for #{cmd_name}]"
+ end
+
+ summary = wrap(summary, summary_width).split "\n"
+ out << sprintf(format, cmd_name, summary.shift)
+ until summary.empty? do
+ out << "#{wrap_indent}#{summary.shift}"
+ end
+ end
+
+ out << nil
+ out << "For help on a particular command, use 'gem help COMMAND'."
+ out << nil
+ out << "Commands may be abbreviated, so long as they are unambiguous."
+ out << "e.g. 'gem i rake' is short for 'gem install rake'."
+
+ say out.join("\n")
+ end
+
+ def show_command_help command_name # :nodoc:
+ command_name = command_name.downcase
+
+ possibilities = @command_manager.find_command_possibilities command_name
+
+ if possibilities.size == 1 then
+ command = @command_manager[possibilities.first]
+ command.invoke("--help")
+ elsif possibilities.size > 1 then
+ alert_warning "Ambiguous command #{command_name} (#{possibilities.join(', ')})"
+ else
+ alert_warning "Unknown command #{command_name}. Try: gem help commands"
+ end
+ end
+
+ def show_help # :nodoc:
+ command = @command_manager[options[:help]]
+ if command then
+ # help with provided command
+ command.invoke("--help")
+ else
+ alert_error "Unknown command #{options[:help]}. Try 'gem help commands'"
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/install_command.rb b/jni/ruby/lib/rubygems/commands/install_command.rb
new file mode 100644
index 0000000..1bf5928
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/install_command.rb
@@ -0,0 +1,347 @@
+require 'rubygems/command'
+require 'rubygems/install_update_options'
+require 'rubygems/dependency_installer'
+require 'rubygems/local_remote_options'
+require 'rubygems/validator'
+require 'rubygems/version_option'
+
+##
+# Gem installer command line tool
+#
+# See `gem help install`
+
+class Gem::Commands::InstallCommand < Gem::Command
+
+ attr_reader :installed_specs # :nodoc:
+
+ include Gem::VersionOption
+ include Gem::LocalRemoteOptions
+ include Gem::InstallUpdateOptions
+
+ def initialize
+ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
+ :format_executable => false,
+ :lock => true,
+ :suggest_alternate => true,
+ :version => Gem::Requirement.default,
+ :without_groups => [],
+ })
+
+ super 'install', 'Install a gem into the local repository', defaults
+
+ add_install_update_options
+ add_local_remote_options
+ add_platform_option
+ add_version_option
+ 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|
+ v = Gem::GEM_DEP_FILES.find do |file|
+ File.exist? file
+ end unless v
+
+ unless v then
+ message = v ? v : "(tried #{Gem::GEM_DEP_FILES.join ', '})"
+
+ raise OptionParser::InvalidArgument,
+ "cannot find gem dependencies file #{message}"
+ end
+
+ o[:gemdeps] = v
+ end
+
+ add_option(:"Install/Update", '--without GROUPS', Array,
+ 'Omit the named groups (comma separated)',
+ 'when installing from a gem dependencies',
+ 'file') do |v,o|
+ o[:without_groups].concat v.map { |without| without.intern }
+ end
+
+ add_option(:"Install/Update", '--default',
+ 'Add the gem\'s full specification to',
+ 'specifications/default and extract only its bin') do |v,o|
+ o[:install_as_default] = v
+ end
+
+ add_option(:"Install/Update", '--explain',
+ 'Rather than install the gems, indicate which would',
+ 'be installed') do |v,o|
+ o[:explain] = v
+ end
+
+ add_option(:"Install/Update", '--[no-]lock',
+ 'Create a lock file (when used with -g/--file)') do |v,o|
+ o[:lock] = v
+ end
+
+ add_option(:"Install/Update", '--[no-]suggestions',
+ 'Suggest alternates when gems are not found') do |v,o|
+ o[:suggest_alternate] = v
+ end
+
+ @installed_specs = []
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to install"
+ end
+
+ def defaults_str # :nodoc:
+ "--both --version '#{Gem::Requirement.default}' --document --no-force\n" +
+ "--install-dir #{Gem.dir} --lock"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The install command installs local or remote gem into a gem repository.
+
+For gems with executables ruby installs a wrapper file into the executable
+directory by default. This can be overridden with the --no-wrappers option.
+The wrapper allows you to choose among alternate gem versions using _version_.
+
+For example `rake _0.7.3_ --version` will run rake version 0.7.3 if a newer
+version is also installed.
+
+Gem Dependency Files
+====================
+
+RubyGems can install a consistent set of gems across multiple environments
+using `gem install -g` when a gem dependencies file (gem.deps.rb, Gemfile or
+Isolate) is present. If no explicit file is given RubyGems attempts to find
+one in the current directory.
+
+When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies
+file the gems from that file will be activated at startup time. Set it to a
+specific filename or to "-" to have RubyGems automatically discover the gem
+dependencies file by walking up from the current directory.
+
+NOTE: Enabling automatic discovery on multiuser systems can lead to
+execution of arbitrary code when used from directories outside your control.
+
+Extension Install Failures
+==========================
+
+If an extension fails to compile during gem installation the gem
+specification is not written out, but the gem remains unpacked in the
+repository. You may need to specify the path to the library's headers and
+libraries to continue. You can do this by adding a -- between RubyGems'
+options and the extension's build options:
+
+ $ gem install some_extension_gem
+ [build fails]
+ Gem files will remain installed in \\
+ /path/to/gems/some_extension_gem-1.0 for inspection.
+ Results logged to /path/to/gems/some_extension_gem-1.0/gem_make.out
+ $ gem install some_extension_gem -- --with-extension-lib=/path/to/lib
+ [build succeeds]
+ $ gem list some_extension_gem
+
+ *** LOCAL GEMS ***
+
+ some_extension_gem (1.0)
+ $
+
+If you correct the compilation errors by editing the gem files you will need
+to write the specification by hand. For example:
+
+ $ gem install some_extension_gem
+ [build fails]
+ Gem files will remain installed in \\
+ /path/to/gems/some_extension_gem-1.0 for inspection.
+ Results logged to /path/to/gems/some_extension_gem-1.0/gem_make.out
+ $ [cd /path/to/gems/some_extension_gem-1.0]
+ $ [edit files or what-have-you and run make]
+ $ gem spec ../../cache/some_extension_gem-1.0.gem --ruby > \\
+ ../../specifications/some_extension_gem-1.0.gemspec
+ $ gem list some_extension_gem
+
+ *** LOCAL GEMS ***
+
+ some_extension_gem (1.0)
+ $
+
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
+ end
+
+ def check_install_dir # :nodoc:
+ if options[:install_dir] and options[:user_install] then
+ alert_error "Use --install-dir or --user-install but not both"
+ terminate_interaction 1
+ end
+ end
+
+ def check_version # :nodoc:
+ if options[:version] != Gem::Requirement.default and
+ get_all_gem_names.size > 1 then
+ alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
+ terminate_interaction 1
+ end
+ end
+
+ def execute
+ if options.include? :gemdeps then
+ install_from_gemdeps
+ return # not reached
+ end
+
+ @installed_specs = []
+
+ ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9'
+
+ check_install_dir
+ check_version
+
+ load_hooks
+
+ exit_code = install_gems
+
+ show_installed
+
+ terminate_interaction exit_code
+ end
+
+ def install_from_gemdeps # :nodoc:
+ require 'rubygems/request_set'
+ rs = Gem::RequestSet.new
+
+ specs = rs.install_from_gemdeps 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
+
+ @installed_specs = specs
+
+ terminate_interaction
+ end
+
+ def install_gem name, version # :nodoc:
+ return if options[:conservative] and
+ not Gem::Dependency.new(name, version).matching_specs.empty?
+
+ req = Gem::Requirement.create(version)
+
+ if options[:ignore_dependencies] then
+ install_gem_without_dependencies name, req
+ else
+ inst = Gem::DependencyInstaller.new options
+ request_set = inst.resolve_dependencies name, req
+
+ if options[:explain]
+ puts "Gems to install:"
+
+ request_set.sorted_requests.each do |s|
+ puts " #{s.full_name}"
+ end
+
+ return
+ else
+ @installed_specs.concat request_set.install options
+ end
+
+ show_install_errors inst.errors
+ end
+ end
+
+ def install_gem_without_dependencies name, req # :nodoc:
+ gem = nil
+
+ if local? then
+ if name =~ /\.gem$/ and File.file? name then
+ source = Gem::Source::SpecificFile.new name
+ spec = source.spec
+ else
+ source = Gem::Source::Local.new
+ spec = source.find_gem name, req
+ end
+ gem = source.download spec if spec
+ end
+
+ if remote? and not gem then
+ dependency = Gem::Dependency.new name, req
+ dependency.prerelease = options[:prerelease]
+
+ fetcher = Gem::RemoteFetcher.fetcher
+ gem = fetcher.download_to_cache dependency
+ end
+
+ inst = Gem::Installer.new gem, options
+ inst.install
+
+ require 'rubygems/dependency_installer'
+ dinst = Gem::DependencyInstaller.new options
+ dinst.installed_gems.replace [inst.spec]
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call dinst, [inst.spec]
+ end unless Gem.done_installing_hooks.empty?
+
+ @installed_specs.push(inst.spec)
+ end
+
+ def install_gems # :nodoc:
+ exit_code = 0
+
+ get_all_gem_names_and_versions.each do |gem_name, gem_version|
+ gem_version ||= options[:version]
+
+ begin
+ install_gem gem_name, gem_version
+ rescue Gem::InstallError => e
+ alert_error "Error installing #{gem_name}:\n\t#{e.message}"
+ exit_code |= 1
+ rescue Gem::GemNotFoundException, Gem::UnsatisfiableDependencyError => e
+ domain = options[:domain]
+ domain = :local unless options[:suggest_alternate]
+ show_lookup_failure e.name, e.version, e.errors, domain
+
+ exit_code |= 2
+ end
+ end
+
+ exit_code
+ end
+
+ ##
+ # Loads post-install hooks
+
+ def load_hooks # :nodoc:
+ if options[:install_as_default]
+ require 'rubygems/install_default_message'
+ else
+ require 'rubygems/install_message'
+ end
+ require 'rubygems/rdoc'
+ end
+
+ def show_install_errors errors # :nodoc:
+ return unless errors
+
+ errors.each do |x|
+ return unless Gem::SourceFetchProblem === x
+
+ msg = "Unable to pull data from '#{x.source.uri}': #{x.error.message}"
+
+ alert_warning msg
+ end
+ end
+
+ def show_installed # :nodoc:
+ return if @installed_specs.empty?
+
+ gems = @installed_specs.length == 1 ? 'gem' : 'gems'
+ say "#{@installed_specs.length} #{gems} installed"
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/list_command.rb b/jni/ruby/lib/rubygems/commands/list_command.rb
new file mode 100644
index 0000000..c6ff237
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/list_command.rb
@@ -0,0 +1,40 @@
+require 'rubygems/command'
+require 'rubygems/commands/query_command'
+
+##
+# An alternate to Gem::Commands::QueryCommand that searches for gems starting
+# with the the supplied argument.
+
+class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
+
+ def initialize
+ super 'list', 'Display local gems whose name matches REGEXP'
+
+ remove_option('--name-matches')
+ end
+
+ def arguments # :nodoc:
+ "REGEXP regexp to look for in gem name"
+ end
+
+ def defaults_str # :nodoc:
+ "--local --no-details"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The list command is used to view the gems you have installed locally.
+
+The --details option displays additional details including the summary, the
+homepage, the author, the locations of different versions of the gem.
+
+To search for remote gems use the search command.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [STRING ...]"
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/lock_command.rb b/jni/ruby/lib/rubygems/commands/lock_command.rb
new file mode 100644
index 0000000..6b4b25a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/lock_command.rb
@@ -0,0 +1,110 @@
+require 'rubygems/command'
+
+class Gem::Commands::LockCommand < Gem::Command
+
+ def initialize
+ super 'lock', 'Generate a lockdown list of gems',
+ :strict => false
+
+ add_option '-s', '--[no-]strict',
+ 'fail if unable to satisfy a dependency' do |strict, options|
+ options[:strict] = strict
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to lock\nVERSION version of gem to lock"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-strict"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The lock command will generate a list of +gem+ statements that will lock down
+the versions for the gem given in the command line. It will specify exact
+versions in the requirements list to ensure that the gems loaded will always
+be consistent. A full recursive search of all effected gems will be
+generated.
+
+Example:
+
+ gem lock rails-1.0.0 > lockdown.rb
+
+will produce in lockdown.rb:
+
+ require "rubygems"
+ gem 'rails', '= 1.0.0'
+ gem 'rake', '= 0.7.0.1'
+ gem 'activesupport', '= 1.2.5'
+ gem 'activerecord', '= 1.13.2'
+ gem 'actionpack', '= 1.11.2'
+ gem 'actionmailer', '= 1.1.5'
+ gem 'actionwebservice', '= 1.0.0'
+
+Just load lockdown.rb from your application to ensure that the current
+versions are loaded. Make sure that lockdown.rb is loaded *before* any
+other require statements.
+
+Notice that rails 1.0.0 only requires that rake 0.6.2 or better be used.
+Rake-0.7.0.1 is the most recent version installed that satisfies that, so we
+lock it down to the exact version.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME-VERSION [GEMNAME-VERSION ...]"
+ end
+
+ def complain(message)
+ if options[:strict] then
+ raise Gem::Exception, message
+ else
+ say "# #{message}"
+ end
+ end
+
+ def execute
+ say "require 'rubygems'"
+
+ locked = {}
+
+ pending = options[:args]
+
+ until pending.empty? do
+ full_name = pending.shift
+
+ spec = Gem::Specification.load spec_path(full_name)
+
+ if spec.nil? then
+ complain "Could not find gem #{full_name}, try using the full name"
+ next
+ end
+
+ say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name]
+ locked[spec.name] = true
+
+ spec.runtime_dependencies.each do |dep|
+ next if locked[dep.name]
+ candidates = dep.matching_specs
+
+ if candidates.empty? then
+ complain "Unable to satisfy '#{dep}' from currently installed gems"
+ else
+ pending << candidates.last.full_name
+ end
+ end
+ end
+ end
+
+ def spec_path(gem_full_name)
+ gemspecs = Gem.path.map { |path|
+ File.join path, "specifications", "#{gem_full_name}.gemspec"
+ }
+
+ gemspecs.find { |path| File.exist? path }
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/mirror_command.rb b/jni/ruby/lib/rubygems/commands/mirror_command.rb
new file mode 100644
index 0000000..24fb668
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/mirror_command.rb
@@ -0,0 +1,25 @@
+require 'rubygems/command'
+
+unless defined? Gem::Commands::MirrorCommand
+ 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 description # :nodoc:
+ <<-EOF
+The mirror command has been moved to the rubygems-mirror gem.
+ EOF
+ end
+
+ def execute
+ alert_error "Install the rubygems-mirror gem for the mirror command"
+ end
+
+ end
+end
diff --git a/jni/ruby/lib/rubygems/commands/open_command.rb b/jni/ruby/lib/rubygems/commands/open_command.rb
new file mode 100644
index 0000000..91963bb
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/open_command.rb
@@ -0,0 +1,74 @@
+require 'English'
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/util'
+
+class Gem::Commands::OpenCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'open', 'Open gem sources in editor'
+
+ add_option('-e', '--editor EDITOR', String,
+ "Opens gem sources in EDITOR") do |editor, options|
+ options[:editor] = editor || get_env_editor
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to open in editor"
+ end
+
+ def defaults_str # :nodoc:
+ "-e #{get_env_editor}"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+ The open command opens gem in editor and changes current path
+ to gem's source directory. Editor can be specified with -e option,
+ otherwise rubygems will look for editor in $EDITOR, $VISUAL and
+ $GEM_EDITOR variables.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [-e EDITOR]"
+ end
+
+ def get_env_editor
+ ENV['GEM_EDITOR'] ||
+ ENV['VISUAL'] ||
+ ENV['EDITOR'] ||
+ 'vi'
+ end
+
+ def execute
+ @version = options[:version] || Gem::Requirement.default
+ @editor = options[:editor] || get_env_editor
+
+ found = open_gem(get_one_gem_name)
+
+ terminate_interaction 1 unless found
+ end
+
+ def open_gem name
+ spec = spec_for name
+ return false unless spec
+
+ open_editor(spec.full_gem_path)
+ end
+
+ def open_editor path
+ system(*@editor.split(/\s+/) + [path])
+ end
+
+ def spec_for name
+ spec = Gem::Specification.find_all_by_name(name, @version).last
+
+ return spec if spec
+
+ say "Unable to find gem '#{name}'"
+ end
+end
diff --git a/jni/ruby/lib/rubygems/commands/outdated_command.rb b/jni/ruby/lib/rubygems/commands/outdated_command.rb
new file mode 100644
index 0000000..7159dbb
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/outdated_command.rb
@@ -0,0 +1,32 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/spec_fetcher'
+require 'rubygems/version_option'
+
+class Gem::Commands::OutdatedCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'outdated', 'Display all gems that need updates'
+
+ add_local_remote_options
+ add_platform_option
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The outdated command lists gems you may wish to upgrade to a newer version.
+
+You can check for dependency mismatches using the dependency command and
+update the gems with the update or install commands.
+ EOF
+ end
+
+ def execute
+ Gem::Specification.outdated_and_latest_version.each do |spec, remote_version|
+ say "#{spec.name} (#{spec.version} < #{remote_version})"
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/commands/owner_command.rb b/jni/ruby/lib/rubygems/commands/owner_command.rb
new file mode 100644
index 0000000..322bf65
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/owner_command.rb
@@ -0,0 +1,99 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/gemcutter_utilities'
+
+class Gem::Commands::OwnerCommand < Gem::Command
+ include Gem::LocalRemoteOptions
+ include Gem::GemcutterUtilities
+
+ def description # :nodoc:
+ <<-EOF
+The owner command lets you add and remove owners of a gem on a push
+server (the default is https://rubygems.org).
+
+The owner of a gem has the permission to push new versions, yank existing
+versions or edit the HTML page of the gem. Be careful of who you give push
+permission to.
+ EOF
+ end
+
+ def arguments # :nodoc:
+ "GEM gem to manage owners for"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEM"
+ end
+
+ def initialize
+ super 'owner', 'Manage gem owners of a gem on the push server'
+ add_proxy_option
+ add_key_option
+ defaults.merge! :add => [], :remove => []
+
+ add_option '-a', '--add EMAIL', 'Add an owner' do |value, options|
+ options[:add] << value
+ end
+
+ add_option '-r', '--remove EMAIL', 'Remove an owner' do |value, options|
+ options[:remove] << value
+ end
+
+ add_option '-h', '--host HOST', 'Use another gemcutter-compatible host' do |value, options|
+ options[:host] = value
+ end
+ end
+
+ def execute
+ @host = options[:host]
+
+ sign_in
+ name = get_one_gem_name
+
+ add_owners name, options[:add]
+ remove_owners name, options[:remove]
+ show_owners name
+ end
+
+ def show_owners name
+ response = rubygems_api_request :get, "api/v1/gems/#{name}/owners.yaml" do |request|
+ request.add_field "Authorization", api_key
+ end
+
+ with_response response do |resp|
+ owners = YAML.load resp.body
+
+ say "Owners for gem: #{name}"
+ owners.each do |owner|
+ say "- #{owner['email']}"
+ end
+ end
+ end
+
+ def add_owners name, owners
+ manage_owners :post, name, owners
+ end
+
+ def remove_owners name, owners
+ manage_owners :delete, name, owners
+ end
+
+ def manage_owners method, name, owners
+ owners.each do |owner|
+ begin
+ response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
+ request.set_form_data 'email' => owner
+ request.add_field "Authorization", api_key
+ end
+
+ action = method == :delete ? "Removing" : "Adding"
+
+ with_response response, "#{action} #{owner}"
+ rescue
+ # ignore
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/pristine_command.rb b/jni/ruby/lib/rubygems/commands/pristine_command.rb
new file mode 100644
index 0000000..dcd5bb7
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/pristine_command.rb
@@ -0,0 +1,165 @@
+require 'rubygems/command'
+require 'rubygems/package'
+require 'rubygems/installer'
+require 'rubygems/version_option'
+
+class Gem::Commands::PristineCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'pristine',
+ 'Restores installed gems to pristine condition from files located in the gem cache',
+ :version => Gem::Requirement.default,
+ :extensions => true,
+ :extensions_set => false,
+ :all => false
+
+ add_option('--all',
+ 'Restore all installed gems to pristine',
+ 'condition') do |value, options|
+ options[:all] = value
+ end
+
+ add_option('--[no-]extensions',
+ 'Restore gems with extensions',
+ 'in addition to regular gems') do |value, options|
+ options[:extensions_set] = true
+ options[:extensions] = value
+ end
+
+ add_option('--only-executables',
+ 'Only restore executables') do |value, options|
+ options[:only_executables] = value
+ end
+
+ add_option('-E', '--[no-]env-shebang',
+ 'Rewrite executables with a shebang',
+ 'of /usr/bin/env') do |value, options|
+ options[:env_shebang] = value
+ end
+
+ add_version_option('restore to', 'pristine condition')
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME gem to restore to pristine condition (unless --all)"
+ end
+
+ def defaults_str # :nodoc:
+ '--extensions'
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The pristine command compares an installed gem with the contents of its
+cached .gem file and restores any files that don't match the cached .gem's
+copy.
+
+If you have made modifications to an installed gem, the pristine command
+will revert them. All extensions are rebuilt and all bin stubs for the gem
+are regenerated after checking for modifications.
+
+If the cached gem cannot be found it will be downloaded.
+
+If --no-extensions is provided pristine will not attempt to restore a gem
+with an extension.
+
+If --extensions is given (but not --all or gem names) only gems with
+extensions will be restored.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [GEMNAME ...]"
+ end
+
+ def execute
+ specs = if options[:all] then
+ Gem::Specification.map
+
+ # `--extensions` must be explicitly given to pristine only gems
+ # with extensions.
+ elsif options[:extensions_set] and
+ options[:extensions] and options[:args].empty? then
+ Gem::Specification.select do |spec|
+ spec.extensions and not spec.extensions.empty?
+ end
+ else
+ get_all_gem_names.map do |gem_name|
+ Gem::Specification.find_all_by_name gem_name, options[:version]
+ end.flatten
+ end
+
+ if specs.to_a.empty? then
+ raise Gem::Exception,
+ "Failed to find gems #{options[:args]} #{options[:version]}"
+ end
+
+ install_dir = Gem.dir # TODO use installer option
+
+ raise Gem::FilePermissionError.new(install_dir) unless
+ File.writable?(install_dir)
+
+ say "Restoring gems to pristine condition..."
+
+ specs.each do |spec|
+ if spec.default_gem?
+ say "Skipped #{spec.full_name}, it is a default gem"
+ next
+ end
+
+ if spec.bundled_gem_in_old_ruby?
+ say "Skipped #{spec.full_name}, it is bundled with old Ruby"
+ next
+ end
+
+ unless spec.extensions.empty? or options[:extensions] then
+ say "Skipped #{spec.full_name}, it needs to compile an extension"
+ next
+ end
+
+ gem = spec.cache_file
+
+ unless File.exist? gem then
+ require 'rubygems/remote_fetcher'
+
+ say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
+
+ dep = Gem::Dependency.new spec.name, spec.version
+ found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
+
+ if found.empty?
+ say "Skipped #{spec.full_name}, it was not found from cache and remote sources"
+ next
+ end
+
+ spec_candidate, source = found.first
+ Gem::RemoteFetcher.fetcher.download spec_candidate, source.uri.to_s, spec.base_dir
+ end
+
+ env_shebang =
+ if options.include? :env_shebang then
+ options[:env_shebang]
+ else
+ install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
+ install_defaults.to_s['--env-shebang']
+ end
+
+ installer = Gem::Installer.new(gem,
+ :wrappers => true,
+ :force => true,
+ :install_dir => spec.base_dir,
+ :env_shebang => env_shebang,
+ :build_args => spec.build_args)
+
+ if options[:only_executables] then
+ installer.generate_bin
+ else
+ installer.install
+ end
+
+ say "Restored #{spec.full_name}"
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/commands/push_command.rb b/jni/ruby/lib/rubygems/commands/push_command.rb
new file mode 100644
index 0000000..6899b48
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/push_command.rb
@@ -0,0 +1,98 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/gemcutter_utilities'
+require 'rubygems/package'
+
+class Gem::Commands::PushCommand < Gem::Command
+ include Gem::LocalRemoteOptions
+ include Gem::GemcutterUtilities
+
+ def description # :nodoc:
+ <<-EOF
+The push command uploads a gem to the push server (the default is
+https://rubygems.org) and adds it to the index.
+
+The gem can be removed from the index (but only the index) using the yank
+command. For further discussion see the help for the yank command.
+ EOF
+ end
+
+ def arguments # :nodoc:
+ "GEM built gem to push up"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEM"
+ end
+
+ def initialize
+ super 'push', 'Push a gem up to the gem server', :host => self.host
+
+ add_proxy_option
+ add_key_option
+
+ add_option('--host HOST',
+ 'Push to another gemcutter-compatible host') do |value, options|
+ options[:host] = value
+ end
+
+ @host = nil
+ end
+
+ def execute
+ @host = options[:host]
+
+ sign_in @host
+
+ send_gem get_one_gem_name
+ end
+
+ def send_gem name
+ args = [:post, "api/v1/gems"]
+
+ latest_rubygems_version = Gem.latest_rubygems_version
+
+ if latest_rubygems_version < Gem.rubygems_version and
+ Gem.rubygems_version.prerelease? and
+ Gem::Version.new('2.0.0.rc.2') != Gem.rubygems_version then
+ alert_error <<-ERROR
+You are using a beta release of RubyGems (#{Gem::VERSION}) which is not
+allowed to push gems. Please downgrade or upgrade to a release version.
+
+The latest released RubyGems version is #{latest_rubygems_version}
+
+You can upgrade or downgrade to the latest release version with:
+
+ gem update --system=#{latest_rubygems_version}
+
+ ERROR
+ terminate_interaction 1
+ end
+
+ gem_data = Gem::Package.new(name)
+
+ unless @host then
+ @host = gem_data.spec.metadata['default_gem_server']
+ end
+
+ # Always include this, even if it's nil
+ args << @host
+
+ if gem_data.spec.metadata.has_key?('allowed_push_host')
+ args << gem_data.spec.metadata['allowed_push_host']
+ end
+
+ say "Pushing gem to #{@host || Gem.host}..."
+
+ response = rubygems_api_request(*args) do |request|
+ request.body = Gem.read_binary name
+ request.add_field "Content-Length", request.body.size
+ request.add_field "Content-Type", "application/octet-stream"
+ request.add_field "Authorization", api_key
+ end
+
+ with_response response
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/query_command.rb b/jni/ruby/lib/rubygems/commands/query_command.rb
new file mode 100644
index 0000000..432250e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/query_command.rb
@@ -0,0 +1,343 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/spec_fetcher'
+require 'rubygems/version_option'
+require 'rubygems/text'
+
+class Gem::Commands::QueryCommand < Gem::Command
+
+ include Gem::Text
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize(name = 'query',
+ summary = 'Query gem information in local or remote repositories')
+ super name, summary,
+ :name => //, :domain => :local, :details => false, :versions => true,
+ :installed => nil, :version => Gem::Requirement.default
+
+ add_option('-i', '--[no-]installed',
+ 'Check for installed gem') do |value, options|
+ options[:installed] = value
+ end
+
+ add_option('-I', 'Equivalent to --no-installed') do |value, options|
+ options[:installed] = false
+ end
+
+ add_version_option command, "for use with --installed"
+
+ add_option('-n', '--name-matches REGEXP',
+ 'Name of gem(s) to query on matches the',
+ 'provided REGEXP') do |value, options|
+ options[:name] = /#{value}/i
+ end
+
+ add_option('-d', '--[no-]details',
+ 'Display detailed information of gem(s)') do |value, options|
+ options[:details] = value
+ end
+
+ add_option( '--[no-]versions',
+ 'Display only gem names') do |value, options|
+ options[:versions] = value
+ options[:details] = false unless value
+ end
+
+ add_option('-a', '--all',
+ 'Display all gem versions') do |value, options|
+ options[:all] = value
+ end
+
+ add_option( '--[no-]prerelease',
+ 'Display prerelease versions') do |value, options|
+ options[:prerelease] = value
+ end
+
+ add_local_remote_options
+ end
+
+ def defaults_str # :nodoc:
+ "--local --name-matches // --no-details --versions --no-installed"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The query command is the basis for the list and search commands.
+
+You should really use the list and search commands instead. This command
+is too hard to use.
+ EOF
+ end
+
+ def execute
+ exit_code = 0
+ if options[:args].to_a.empty? and options[:name].source.empty?
+ name = options[:name]
+ no_name = true
+ elsif !options[:name].source.empty?
+ name = Array(options[:name])
+ else
+ name = options[:args].to_a.map{|arg| /#{arg}/i }
+ end
+
+ prerelease = options[:prerelease]
+
+ unless options[:installed].nil? then
+ if no_name then
+ alert_error "You must specify a gem name"
+ exit_code |= 4
+ elsif name.count > 1
+ alert_error "You must specify only ONE gem!"
+ exit_code |= 4
+ else
+ installed = installed? name.first, options[:version]
+ installed = !installed unless options[:installed]
+
+ if installed then
+ say "true"
+ else
+ say "false"
+ exit_code |= 1
+ end
+ end
+
+ terminate_interaction exit_code
+ end
+
+ names = Array(name)
+ names.each { |n| show_gems n, prerelease }
+ end
+
+ private
+
+ def display_header type
+ if (ui.outs.tty? and Gem.configuration.verbose) or both? then
+ say
+ say "*** #{type} GEMS ***"
+ say
+ end
+ end
+
+ #Guts of original execute
+ def show_gems name, prerelease
+ req = Gem::Requirement.default
+ # TODO: deprecate for real
+ dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req }
+ dep.prerelease = prerelease
+
+ if local? then
+ if prerelease and not both? then
+ alert_warning "prereleases are always shown locally"
+ end
+
+ display_header 'LOCAL'
+
+ specs = Gem::Specification.find_all { |s|
+ s.name =~ name and req =~ s.version
+ }
+
+ spec_tuples = specs.map do |spec|
+ [spec.name_tuple, spec]
+ end
+
+ output_query_results spec_tuples
+ end
+
+ if remote? then
+ display_header 'REMOTE'
+
+ fetcher = Gem::SpecFetcher.fetcher
+
+ type = if options[:all]
+ if options[:prerelease]
+ :complete
+ else
+ :released
+ end
+ elsif options[:prerelease]
+ :prerelease
+ else
+ :latest
+ end
+
+ if name.source.empty?
+ spec_tuples = fetcher.detect(type) { true }
+ else
+ spec_tuples = fetcher.detect(type) do |name_tuple|
+ name === name_tuple.name
+ end
+ end
+
+ output_query_results spec_tuples
+ end
+ end
+
+ ##
+ # Check if gem +name+ version +version+ is installed.
+
+ def installed?(name, req = Gem::Requirement.default)
+ Gem::Specification.any? { |s| s.name =~ name and req =~ s.version }
+ end
+
+ def output_query_results(spec_tuples)
+ output = []
+ versions = Hash.new { |h,name| h[name] = [] }
+
+ spec_tuples.each do |spec_tuple, source|
+ versions[spec_tuple.name] << [spec_tuple, source]
+ end
+
+ versions = versions.sort_by do |(n,_),_|
+ n.downcase
+ end
+
+ output_versions output, versions
+
+ say output.join(options[:details] ? "\n\n" : "\n")
+ end
+
+ def output_versions output, versions
+ versions.each do |gem_name, matching_tuples|
+ matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse
+
+ platforms = Hash.new { |h,version| h[version] = [] }
+
+ matching_tuples.each do |n, _|
+ platforms[n.version] << n.platform if n.platform
+ end
+
+ seen = {}
+
+ matching_tuples.delete_if do |n,_|
+ if seen[n.version] then
+ true
+ else
+ seen[n.version] = true
+ false
+ end
+ end
+
+ output << make_entry(matching_tuples, platforms)
+ end
+ end
+
+ def entry_details entry, detail_tuple, specs, platforms
+ return unless options[:details]
+
+ name_tuple, spec = detail_tuple
+
+ spec = spec.fetch_spec name_tuple unless Gem::Specification === spec
+
+ entry << "\n"
+
+ spec_platforms entry, platforms
+ spec_authors entry, spec
+ spec_homepage entry, spec
+ spec_license entry, spec
+ spec_loaded_from entry, spec, specs
+ spec_summary entry, spec
+ end
+
+ def entry_versions entry, name_tuples, platforms
+ return unless options[:versions]
+
+ list =
+ if platforms.empty? or options[:details] then
+ name_tuples.map { |n| n.version }.uniq
+ else
+ platforms.sort.reverse.map do |version, pls|
+ if pls == [Gem::Platform::RUBY] then
+ version
+ else
+ ruby = pls.delete Gem::Platform::RUBY
+ platform_list = [ruby, *pls.sort].compact
+ "#{version} #{platform_list.join ' '}"
+ end
+ end
+ end
+
+ entry << " (#{list.join ', '})"
+ end
+
+ def make_entry entry_tuples, platforms
+ detail_tuple = entry_tuples.first
+
+ name_tuples, specs = entry_tuples.flatten.partition do |item|
+ Gem::NameTuple === item
+ end
+
+ entry = [name_tuples.first.name]
+
+ entry_versions entry, name_tuples, platforms
+ entry_details entry, detail_tuple, specs, platforms
+
+ entry.join
+ end
+
+ def spec_authors entry, spec
+ authors = "Author#{spec.authors.length > 1 ? 's' : ''}: "
+ authors << spec.authors.join(', ')
+ entry << format_text(authors, 68, 4)
+ end
+
+ def spec_homepage entry, spec
+ return if spec.homepage.nil? or spec.homepage.empty?
+
+ entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
+ end
+
+ def spec_license entry, spec
+ return if spec.license.nil? or spec.license.empty?
+
+ licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: "
+ licenses << spec.licenses.join(', ')
+ entry << "\n" << format_text(licenses, 68, 4)
+ end
+
+ def spec_loaded_from entry, spec, specs
+ return unless spec.loaded_from
+
+ if specs.length == 1 then
+ default = spec.default_gem? ? ' (default)' : nil
+ entry << "\n" << " Installed at#{default}: #{spec.base_dir}"
+ else
+ label = 'Installed at'
+ specs.each do |s|
+ version = s.version.to_s
+ version << ', default' if s.default_gem?
+ entry << "\n" << " #{label} (#{version}): #{s.base_dir}"
+ label = ' ' * label.length
+ end
+ end
+ end
+
+ def spec_platforms entry, platforms
+ non_ruby = platforms.any? do |_, pls|
+ pls.any? { |pl| pl != Gem::Platform::RUBY }
+ end
+
+ return unless non_ruby
+
+ if platforms.length == 1 then
+ title = platforms.values.length == 1 ? 'Platform' : 'Platforms'
+ entry << " #{title}: #{platforms.values.sort.join ', '}\n"
+ else
+ entry << " Platforms:\n"
+ platforms.sort_by do |version,|
+ version
+ end.each do |version, pls|
+ label = " #{version}: "
+ data = format_text pls.sort.join(', '), 68, label.length
+ data[0, label.length] = label
+ entry << data << "\n"
+ end
+ end
+ end
+
+ def spec_summary entry, spec
+ entry << "\n\n" << format_text(spec.summary, 68, 4)
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/rdoc_command.rb b/jni/ruby/lib/rubygems/commands/rdoc_command.rb
new file mode 100644
index 0000000..86597f9
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/rdoc_command.rb
@@ -0,0 +1,96 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/rdoc'
+require 'fileutils'
+
+class Gem::Commands::RdocCommand < Gem::Command
+ include Gem::VersionOption
+
+ def initialize
+ super 'rdoc', 'Generates RDoc for pre-installed gems',
+ :version => Gem::Requirement.default,
+ :include_rdoc => false, :include_ri => true, :overwrite => false
+
+ add_option('--all',
+ 'Generate RDoc/RI documentation for all',
+ 'installed gems') do |value, options|
+ options[:all] = value
+ end
+
+ add_option('--[no-]rdoc',
+ 'Generate RDoc HTML') do |value, options|
+ options[:include_rdoc] = value
+ end
+
+ add_option('--[no-]ri',
+ 'Generate RI data') do |value, options|
+ options[:include_ri] = value
+ end
+
+ add_option('--[no-]overwrite',
+ 'Overwrite installed documents') do |value, options|
+ options[:overwrite] = value
+ end
+
+ add_version_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME gem to generate documentation for (unless --all)"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}' --ri --no-overwrite"
+ end
+
+ def description # :nodoc:
+ <<-DESC
+The rdoc command builds documentation for installed gems. By default
+only documentation is built using rdoc, but additional types of
+documentation may be built through rubygems plugins and the
+Gem.post_installs hook.
+
+Use --overwrite to force rebuilding of documentation.
+ DESC
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [args]"
+ end
+
+ def execute
+ specs = if options[:all] then
+ Gem::Specification.to_a
+ else
+ get_all_gem_names.map do |name|
+ Gem::Specification.find_by_name name, options[:version]
+ end.flatten.uniq
+ end
+
+ if specs.empty? then
+ alert_error 'No matching gems found'
+ terminate_interaction 1
+ end
+
+ specs.each do |spec|
+ doc = Gem::RDoc.new spec, options[:include_rdoc], options[:include_ri]
+
+ doc.force = options[:overwrite]
+
+ if options[:overwrite] then
+ FileUtils.rm_rf File.join(spec.doc_dir, 'ri')
+ FileUtils.rm_rf File.join(spec.doc_dir, 'rdoc')
+ end
+
+ begin
+ doc.generate
+ rescue Errno::ENOENT => e
+ e.message =~ / - /
+ alert_error "Unable to document #{spec.full_name}, #{$'} is missing, skipping"
+ terminate_interaction 1 if specs.length == 1
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/search_command.rb b/jni/ruby/lib/rubygems/commands/search_command.rb
new file mode 100644
index 0000000..a1e2c1a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/search_command.rb
@@ -0,0 +1,40 @@
+require 'rubygems/command'
+require 'rubygems/commands/query_command'
+
+class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
+
+ def initialize
+ super 'search', 'Display remote gems whose name matches REGEXP'
+
+ remove_option '--name-matches'
+
+ defaults[:domain] = :remote
+ end
+
+ def arguments # :nodoc:
+ "REGEXP regexp to search for in gem name"
+ end
+
+ def defaults_str # :nodoc:
+ "--remote --no-details"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The search command displays remote gems whose name matches the given
+regexp.
+
+The --details option displays additional details from the gem but will
+take a little longer to complete as it must download the information
+individually from the index.
+
+To list local gems use the list command.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [REGEXP]"
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/server_command.rb b/jni/ruby/lib/rubygems/commands/server_command.rb
new file mode 100644
index 0000000..4796ce2
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/server_command.rb
@@ -0,0 +1,86 @@
+require 'rubygems/command'
+require 'rubygems/server'
+
+class Gem::Commands::ServerCommand < Gem::Command
+
+ def initialize
+ super 'server', 'Documentation and gem repository HTTP server',
+ :port => 8808, :gemdir => [], :daemon => false
+
+ OptionParser.accept :Port do |port|
+ if port =~ /\A\d+\z/ then
+ port = Integer port
+ raise OptionParser::InvalidArgument, "#{port}: not a port number" if
+ port > 65535
+
+ port
+ else
+ begin
+ Socket.getservbyname port
+ rescue SocketError
+ raise OptionParser::InvalidArgument, "#{port}: no such named service"
+ end
+ end
+ end
+
+ add_option '-p', '--port=PORT', :Port,
+ 'port to listen on' do |port, options|
+ options[:port] = port
+ end
+
+ add_option '-d', '--dir=GEMDIR',
+ 'directories from which to serve gems',
+ 'multiple directories may be provided' do |gemdir, options|
+ options[:gemdir] << File.expand_path(gemdir)
+ end
+
+ add_option '--[no-]daemon', 'run as a daemon' do |daemon, options|
+ options[:daemon] = daemon
+ end
+
+ add_option '-b', '--bind=HOST,HOST',
+ 'addresses to bind', Array do |address, options|
+ options[:addresses] ||= []
+ options[:addresses].push(*address)
+ end
+
+ add_option '-l', '--launch[=COMMAND]',
+ 'launches a browser window',
+ "COMMAND defaults to 'start' on Windows",
+ "and 'open' on all other platforms" do |launch, options|
+ launch ||= Gem.win_platform? ? 'start' : 'open'
+ options[:launch] = launch
+ end
+ end
+
+ def defaults_str # :nodoc:
+ "--port 8808 --dir #{Gem.dir} --no-daemon"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The server command starts up a web server that hosts the RDoc for your
+installed gems and can operate as a server for installation of gems on other
+machines.
+
+The cache files for installed gems must exist to use the server as a source
+for gem installation.
+
+To install gems from a running server, use `gem install GEMNAME --source
+http://gem_server_host:8808`
+
+You can set up a shortcut to gem server documentation using the URL:
+
+ http://localhost:8808/rdoc?q=%s - Firefox
+ http://localhost:8808/rdoc?q=* - LaunchBar
+
+ EOF
+ end
+
+ def execute
+ options[:gemdir] = Gem.path if options[:gemdir].empty?
+ Gem::Server.run options
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/setup_command.rb b/jni/ruby/lib/rubygems/commands/setup_command.rb
new file mode 100644
index 0000000..6617396
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/setup_command.rb
@@ -0,0 +1,483 @@
+require 'rubygems/command'
+
+##
+# Installs RubyGems itself. This command is ordinarily only available from a
+# RubyGems checkout or tarball.
+
+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
+ require 'tmpdir'
+
+ super 'setup', 'Install RubyGems',
+ :format_executable => true, :document => %w[ri],
+ :site_or_vendor => 'sitelibdir',
+ :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',
+ 'Prefix path for installing RubyGems',
+ 'Will not affect gem repository location' do |prefix, options|
+ options[:prefix] = File.expand_path prefix
+ end
+
+ add_option '--destdir=DESTDIR',
+ 'Root directory to install RubyGems into',
+ 'Mainly used for packaging RubyGems' do |destdir, options|
+ options[:destdir] = File.expand_path destdir
+ end
+
+ add_option '--[no-]vendor',
+ 'Install into vendorlibdir not sitelibdir' do |vendor, options|
+ options[:site_or_vendor] = vendor ? 'vendorlibdir' : 'sitelibdir'
+ end
+
+ add_option '--[no-]format-executable',
+ 'Makes `gem` match ruby',
+ 'If ruby is ruby18, gem will be gem18' do |value, options|
+ options[:format_executable] = value
+ 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',
+ 'Generate RDoc documentation for RubyGems' do |value, options|
+ if value then
+ options[:document] << 'rdoc'
+ else
+ options[:document].delete 'rdoc'
+ end
+
+ options[:document].uniq!
+ end
+
+ add_option '--[no-]ri',
+ 'Generate RI documentation for RubyGems' do |value, options|
+ if value then
+ options[:document] << 'ri'
+ else
+ options[:document].delete 'ri'
+ end
+
+ options[:document].uniq!
+ end
+
+ @verbose = nil
+ end
+
+ def check_ruby_version
+ required_version = Gem::Requirement.new '>= 1.8.7'
+
+ unless required_version.satisfied_by? Gem.ruby_version then
+ alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}"
+ terminate_interaction 1
+ end
+ end
+
+ def defaults_str # :nodoc:
+ "--format-executable --document ri"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+Installs RubyGems itself.
+
+RubyGems installs RDoc for itself in GEM_HOME. By default this is:
+ #{Gem.dir}
+
+If you prefer a different directory, set the GEM_HOME environment variable.
+
+RubyGems will install the gem command with a name matching ruby's
+prefix and suffix. If ruby was installed as `ruby18`, gem will be
+installed as `gem18`.
+
+By default, this RubyGems will install gem as:
+ #{Gem.default_exec_format % 'gem'}
+ EOF
+ end
+
+ def execute
+ @verbose = Gem.configuration.really_verbose
+
+ install_destdir = options[:destdir]
+
+ unless install_destdir.empty? then
+ ENV['GEM_HOME'] ||= File.join(install_destdir,
+ Gem.default_dir.gsub(/^[a-zA-Z]:/, ''))
+ end
+
+ check_ruby_version
+
+ require 'fileutils'
+ if Gem.configuration.really_verbose then
+ extend FileUtils::Verbose
+ else
+ extend FileUtils
+ end
+
+ lib_dir, bin_dir = make_destination_dirs install_destdir
+
+ install_lib lib_dir
+
+ install_executables bin_dir
+
+ remove_old_bin_files bin_dir
+
+ remove_old_lib_files lib_dir
+
+ say "RubyGems #{Gem::VERSION} installed"
+
+ uninstall_old_gemcutter
+
+ documentation_success = install_rdoc
+
+ say
+ if @verbose then
+ say "-" * 78
+ say
+ 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])
+
+ show_release_notes
+
+ say
+ say "-" * 78
+ say
+
+ say "RubyGems installed the following executables:"
+ say @bin_file_names.map { |name| "\t#{name}\n" }
+ say
+
+ unless @bin_file_names.grep(/#{File::SEPARATOR}gem$/) then
+ say "If `gem` was installed by a previous RubyGems installation, you may need"
+ say "to remove it by hand."
+ say
+ 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
+
+ def install_executables(bin_dir)
+ say "Installing gem executable" if @verbose
+
+ @bin_file_names = []
+
+ Dir.chdir 'bin' do
+ bin_files = Dir['*']
+
+ bin_files.delete 'update_rubygems'
+
+ bin_files.each do |bin_file|
+ bin_file_formatted = if options[:format_executable] then
+ Gem.default_exec_format % bin_file
+ else
+ bin_file
+ end
+
+ dest_file = File.join bin_dir, bin_file_formatted
+ bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
+
+ begin
+ bin = File.readlines bin_file
+ bin[0] = "#!#{Gem.ruby}\n"
+
+ File.open bin_tmp_file, 'w' do |fp|
+ fp.puts bin.join
+ end
+
+ install bin_tmp_file, dest_file, :mode => 0755
+ @bin_file_names << dest_file
+ ensure
+ rm bin_tmp_file
+ end
+
+ next unless Gem.win_platform?
+
+ begin
+ bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
+
+ File.open bin_cmd_file, 'w' do |file|
+ file.puts <<-TEXT
+@ECHO OFF
+IF NOT "%~f0" == "~f0" GOTO :WinNT
+@"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+GOTO :EOF
+:WinNT
+@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
+TEXT
+ end
+
+ install bin_cmd_file, "#{dest_file}.bat", :mode => 0755
+ ensure
+ rm bin_cmd_file
+ end
+ end
+ end
+ end
+
+ def install_file file, dest_dir
+ dest_file = File.join dest_dir, file
+ dest_dir = File.dirname dest_file
+ mkdir_p dest_dir unless File.directory? dest_dir
+
+ install file, dest_file, :mode => 0644
+ end
+
+ def install_lib(lib_dir)
+ say "Installing RubyGems" if @verbose
+
+ lib_files = rb_files_in 'lib'
+ pem_files = pem_files_in 'lib'
+
+ Dir.chdir 'lib' do
+ lib_files.each do |lib_file|
+ install_file lib_file, lib_dir
+ end
+
+ pem_files.each do |pem_file|
+ install_file pem_file, lib_dir
+ end
+ end
+ end
+
+ def install_rdoc
+ gem_doc_dir = File.join Gem.dir, 'doc'
+ rubygems_name = "rubygems-#{Gem::VERSION}"
+ 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
+ (not File.exist? rubygems_doc_dir or
+ File.writable? rubygems_doc_dir) then
+ say "Removing old RubyGems RDoc and ri" if @verbose
+ Dir[File.join(Gem.dir, 'doc', 'rubygems-[0-9]*')].each do |dir|
+ rm_rf dir
+ end
+
+ require 'rubygems/rdoc'
+
+ fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION
+ def fake_spec.full_gem_path
+ File.expand_path '../../../..', __FILE__
+ end
+
+ generate_ri = options[:document].include? 'ri'
+ generate_rdoc = options[:document].include? 'rdoc'
+
+ rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri
+ rdoc.generate
+
+ return true
+ elsif @verbose then
+ say "Skipping RDoc generation, #{gem_doc_dir} not writable"
+ say "Set the GEM_HOME environment variable if you want RDoc generated"
+ end
+
+ return false
+ end
+
+ def make_destination_dirs(install_destdir)
+ lib_dir, bin_dir = Gem.default_rubygems_dirs
+
+ unless lib_dir
+ lib_dir, bin_dir = generate_default_dirs(install_destdir)
+ end
+
+ mkdir_p lib_dir
+ mkdir_p bin_dir
+
+ return lib_dir, bin_dir
+ end
+
+ def generate_default_dirs(install_destdir)
+ prefix = options[:prefix]
+ site_or_vendor = options[:site_or_vendor]
+
+ if prefix.empty? then
+ lib_dir = RbConfig::CONFIG[site_or_vendor]
+ bin_dir = RbConfig::CONFIG['bindir']
+ else
+ # Apple installed RubyGems into libdir, and RubyGems <= 1.1.0 gets
+ # confused about installation location, so switch back to
+ # sitelibdir/vendorlibdir.
+ if defined?(APPLE_GEM_HOME) and
+ # just in case Apple and RubyGems don't get this patched up proper.
+ (prefix == RbConfig::CONFIG['libdir'] or
+ # this one is important
+ prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) then
+ lib_dir = RbConfig::CONFIG[site_or_vendor]
+ bin_dir = RbConfig::CONFIG['bindir']
+ else
+ lib_dir = File.join prefix, 'lib'
+ bin_dir = File.join prefix, 'bin'
+ end
+ end
+
+ unless install_destdir.empty? then
+ lib_dir = File.join install_destdir, lib_dir.gsub(/^[a-zA-Z]:/, '')
+ bin_dir = File.join install_destdir, bin_dir.gsub(/^[a-zA-Z]:/, '')
+ end
+
+ [lib_dir, bin_dir]
+ end
+
+ def pem_files_in dir
+ Dir.chdir dir do
+ Dir[File.join('**', '*pem')]
+ end
+ end
+
+ def rb_files_in dir
+ Dir.chdir dir do
+ Dir[File.join('**', '*rb')]
+ end
+ end
+
+ def remove_old_bin_files(bin_dir)
+ old_bin_files = {
+ 'gem_mirror' => 'gem mirror',
+ 'gem_server' => 'gem server',
+ 'gemlock' => 'gem lock',
+ 'gemri' => 'ri',
+ 'gemwhich' => 'gem which',
+ 'index_gem_repository.rb' => 'gem generate_index',
+ }
+
+ old_bin_files.each do |old_bin_file, new_name|
+ old_bin_path = File.join bin_dir, old_bin_file
+ next unless File.exist? old_bin_path
+
+ deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."
+
+ File.open old_bin_path, 'w' do |fp|
+ fp.write <<-EOF
+#!#{Gem.ruby}
+
+abort "#{deprecation_message}"
+ EOF
+ end
+
+ next unless Gem.win_platform?
+
+ File.open "#{old_bin_path}.bat", 'w' do |fp|
+ fp.puts %{@ECHO.#{deprecation_message}}
+ end
+ end
+ end
+
+ def remove_old_lib_files lib_dir
+ rubygems_dir = File.join lib_dir, 'rubygems'
+ lib_files = rb_files_in 'lib/rubygems'
+
+ old_lib_files = rb_files_in rubygems_dir
+
+ to_remove = old_lib_files - lib_files
+
+ to_remove.delete_if do |file|
+ file.start_with? 'defaults'
+ end
+
+ Dir.chdir rubygems_dir do
+ to_remove.each do |file|
+ FileUtils.rm_f file
+
+ warn "unable to remove old file #{file} please remove it by hand" if
+ File.exist? file
+ end
+ end
+ end
+
+ def show_release_notes
+ release_notes = File.join Dir.pwd, 'History.txt'
+
+ release_notes =
+ if File.exist? release_notes then
+ history = File.read release_notes
+
+ history.force_encoding Encoding::UTF_8 if
+ Object.const_defined? :Encoding
+
+ history = history.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 do |x|
+ Gem::Version.new(x)
+ end
+
+ history_string = ""
+
+ until versions.length == 0 or
+ versions.shift < options[:previous_version] do
+ history_string += version_lines.shift + text.shift
+ end
+
+ history_string
+ else
+ "Oh-no! Unable to find release notes!"
+ end
+
+ say release_notes
+ end
+
+ def uninstall_old_gemcutter
+ require 'rubygems/uninstaller'
+
+ ui = Gem::Uninstaller.new('gemcutter', :all => true, :ignore => true,
+ :version => '< 0.4')
+ ui.uninstall
+ rescue Gem::InstallError
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/sources_command.rb b/jni/ruby/lib/rubygems/commands/sources_command.rb
new file mode 100644
index 0000000..81ff07b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/sources_command.rb
@@ -0,0 +1,210 @@
+require 'rubygems/command'
+require 'rubygems/remote_fetcher'
+require 'rubygems/spec_fetcher'
+require 'rubygems/local_remote_options'
+
+class Gem::Commands::SourcesCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+
+ def initialize
+ require 'fileutils'
+
+ super 'sources',
+ 'Manage the sources and cache file RubyGems uses to search for gems'
+
+ add_option '-a', '--add SOURCE_URI', 'Add source' do |value, options|
+ options[:add] = value
+ end
+
+ add_option '-l', '--list', 'List sources' do |value, options|
+ options[:list] = value
+ end
+
+ add_option '-r', '--remove SOURCE_URI', 'Remove source' do |value, options|
+ options[:remove] = value
+ end
+
+ add_option '-c', '--clear-all',
+ 'Remove all sources (clear the cache)' do |value, options|
+ options[:clear_all] = value
+ end
+
+ add_option '-u', '--update', 'Update source cache' do |value, options|
+ options[:update] = value
+ end
+
+ add_proxy_option
+ end
+
+ def add_source source_uri # :nodoc:
+ check_rubygems_https source_uri
+
+ source = Gem::Source.new source_uri
+
+ begin
+ if Gem.sources.include? source_uri then
+ say "source #{source_uri} already present in the cache"
+ else
+ source.load_specs :released
+ Gem.sources << source
+ Gem.configuration.write
+
+ say "#{source_uri} added to sources"
+ end
+ rescue URI::Error, ArgumentError
+ say "#{source_uri} is not a URI"
+ terminate_interaction 1
+ rescue Gem::RemoteFetcher::FetchError => e
+ say "Error fetching #{source_uri}:\n\t#{e.message}"
+ terminate_interaction 1
+ end
+ end
+
+ def check_rubygems_https source_uri # :nodoc:
+ uri = URI source_uri
+
+ if uri.scheme and uri.scheme.downcase == 'http' and
+ uri.host.downcase == 'rubygems.org' then
+ question = <<-QUESTION.chomp
+https://rubygems.org is recommended for security over #{uri}
+
+Do you want to add this insecure source?
+ QUESTION
+
+ terminate_interaction 1 unless ask_yes_no question
+ end
+ end
+
+ def clear_all # :nodoc:
+ path = Gem.spec_cache_dir
+ FileUtils.rm_rf path
+
+ unless File.exist? path then
+ say "*** Removed specs cache ***"
+ else
+ unless File.writable? path then
+ say "*** Unable to remove source cache (write protected) ***"
+ else
+ say "*** Unable to remove source cache ***"
+ end
+
+ terminate_interaction 1
+ end
+ end
+
+ def defaults_str # :nodoc:
+ '--list'
+ end
+
+ def description # :nodoc:
+ <<-EOF
+RubyGems fetches gems from the sources you have configured (stored in your
+~/.gemrc).
+
+The default source is https://rubygems.org, but you may have older sources
+configured. This guide will help you update your sources or configure
+yourself to use your own gem server.
+
+Without any arguments the sources lists your currently configured sources:
+
+ $ gem sources
+ *** CURRENT SOURCES ***
+
+ https://rubygems.org
+
+This may list multiple sources or non-rubygems sources. You probably
+configured them before or have an old `~/.gemrc`. If you have sources you
+do not recognize you should remove them.
+
+RubyGems has been configured to serve gems via the following URLs through
+its history:
+
+* http://gems.rubyforge.org (RubyGems 1.3.6 and earlier)
+* http://rubygems.org (RubyGems 1.3.7 through 1.8.25)
+* https://rubygems.org (RubyGems 2.0.1 and newer)
+
+Since all of these sources point to the same set of gems you only need one
+of them in your list. https://rubygems.org is recommended as it brings the
+protections of an SSL connection to gem downloads.
+
+To add a source use the --add argument:
+
+ $ gem sources --add https://rubygems.org
+ https://rubygems.org added to sources
+
+RubyGems will check to see if gems can be installed from the source given
+before it is added.
+
+To remove a source use the --remove argument:
+
+ $ gem sources --remove http://rubygems.org
+ http://rubygems.org removed from sources
+
+ EOF
+ end
+
+ def list # :nodoc:
+ say "*** CURRENT SOURCES ***"
+ say
+
+ Gem.sources.each do |src|
+ say src
+ end
+ end
+
+ def list? # :nodoc:
+ !(options[:add] ||
+ options[:clear_all] ||
+ options[:remove] ||
+ options[:update])
+ end
+
+ def execute
+ clear_all if options[:clear_all]
+
+ source_uri = options[:add]
+ add_source source_uri if source_uri
+
+ source_uri = options[:remove]
+ remove_source source_uri if source_uri
+
+ update if options[:update]
+
+ list if list?
+ end
+
+ def remove_source source_uri # :nodoc:
+ unless Gem.sources.include? source_uri then
+ say "source #{source_uri} not present in cache"
+ else
+ Gem.sources.delete source_uri
+ Gem.configuration.write
+
+ say "#{source_uri} removed from sources"
+ end
+ end
+
+ def update # :nodoc:
+ Gem.sources.each_source do |src|
+ src.load_specs :released
+ src.load_specs :latest
+ end
+
+ say "source cache successfully updated"
+ end
+
+ def remove_cache_file desc, path # :nodoc:
+ FileUtils.rm_rf path
+
+ if not File.exist?(path) then
+ say "*** Removed #{desc} source cache ***"
+ elsif not File.writable?(path) then
+ say "*** Unable to remove #{desc} source cache (write protected) ***"
+ else
+ say "*** Unable to remove #{desc} source cache ***"
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/specification_command.rb b/jni/ruby/lib/rubygems/commands/specification_command.rb
new file mode 100644
index 0000000..3bc02a9
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/specification_command.rb
@@ -0,0 +1,145 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/version_option'
+require 'rubygems/package'
+
+class Gem::Commands::SpecificationCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ Gem.load_yaml
+
+ super 'specification', 'Display gem specification (in yaml)',
+ :domain => :local, :version => Gem::Requirement.default,
+ :format => :yaml
+
+ add_version_option('examine')
+ add_platform_option
+ add_prerelease_option
+
+ add_option('--all', 'Output specifications for all versions of',
+ 'the gem') do |value, options|
+ options[:all] = true
+ end
+
+ add_option('--ruby', 'Output ruby format') do |value, options|
+ options[:format] = :ruby
+ end
+
+ add_option('--yaml', 'Output YAML format') do |value, options|
+ options[:format] = :yaml
+ end
+
+ add_option('--marshal', 'Output Marshal format') do |value, options|
+ options[:format] = :marshal
+ end
+
+ add_local_remote_options
+ end
+
+ def arguments # :nodoc:
+ <<-ARGS
+GEMFILE name of gem to show the gemspec for
+FIELD name of gemspec field to show
+ ARGS
+ end
+
+ def defaults_str # :nodoc:
+ "--local --version '#{Gem::Requirement.default}' --yaml"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The specification command allows you to extract the specification from
+a gem for examination.
+
+The specification can be output in YAML, ruby or Marshal formats.
+
+Specific fields in the specification can be extracted in YAML format:
+
+ $ gem spec rake summary
+ --- Ruby based make-like utility.
+ ...
+
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [GEMFILE] [FIELD]"
+ end
+
+ def execute
+ specs = []
+ gem = options[:args].shift
+
+ unless gem then
+ raise Gem::CommandLineError,
+ "Please specify a gem name or file on the command line"
+ end
+
+ case v = options[:version]
+ when String
+ req = Gem::Requirement.create v
+ when Gem::Requirement
+ req = v
+ else
+ raise Gem::CommandLineError, "Unsupported version type: '#{v}'"
+ end
+
+ if !req.none? and options[:all]
+ alert_error "Specify --all or -v, not both"
+ terminate_interaction 1
+ end
+
+ if options[:all]
+ dep = Gem::Dependency.new gem
+ else
+ dep = Gem::Dependency.new gem, req
+ end
+
+ field = get_one_optional_argument
+
+ raise Gem::CommandLineError, "--ruby and FIELD are mutually exclusive" if
+ field and options[:format] == :ruby
+
+ if local? then
+ if File.exist? gem then
+ specs << Gem::Package.new(gem).spec rescue nil
+ end
+
+ if specs.empty? then
+ specs.push(*dep.matching_specs)
+ end
+ end
+
+ if remote? then
+ dep.prerelease = options[:prerelease]
+ found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
+
+ specs.push(*found.map { |spec,| spec })
+ end
+
+ if specs.empty? then
+ alert_error "No gem matching '#{dep}' found"
+ terminate_interaction 1
+ end
+
+ unless options[:all] then
+ specs = [specs.max_by { |s| s.version }]
+ end
+
+ specs.each do |s|
+ s = s.send field if field
+
+ say case options[:format]
+ when :ruby then s.to_ruby
+ when :marshal then Marshal.dump s
+ else s.to_yaml
+ end
+
+ say "\n"
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/commands/stale_command.rb b/jni/ruby/lib/rubygems/commands/stale_command.rb
new file mode 100644
index 0000000..0ef0755
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/stale_command.rb
@@ -0,0 +1,38 @@
+require 'rubygems/command'
+
+class Gem::Commands::StaleCommand < Gem::Command
+ def initialize
+ super('stale', 'List gems along with access times')
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The stale command lists the latest access time for all the files in your
+installed gems.
+
+You can use this command to discover gems and gem versions you are no
+longer using.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name}"
+ end
+
+ def execute
+ gem_to_atime = {}
+ Gem::Specification.each do |spec|
+ name = spec.full_name
+ Dir["#{spec.full_gem_path}/**/*.*"].each do |file|
+ next if File.directory?(file)
+ stat = File.stat(file)
+ gem_to_atime[name] ||= stat.atime
+ gem_to_atime[name] = stat.atime if gem_to_atime[name] < stat.atime
+ end
+ end
+
+ gem_to_atime.sort_by { |_, atime| atime }.each do |name, atime|
+ say "#{name} at #{atime.strftime '%c'}"
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/commands/uninstall_command.rb b/jni/ruby/lib/rubygems/commands/uninstall_command.rb
new file mode 100644
index 0000000..9285e57
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/uninstall_command.rb
@@ -0,0 +1,164 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/uninstaller'
+require 'fileutils'
+
+##
+# Gem uninstaller command line tool
+#
+# See `gem help uninstall`
+
+class Gem::Commands::UninstallCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'uninstall', 'Uninstall gems from the local repository',
+ :version => Gem::Requirement.default, :user_install => true,
+ :check_dev => false, :vendor => false
+
+ add_option('-a', '--[no-]all',
+ 'Uninstall all matching versions'
+ ) do |value, options|
+ options[:all] = value
+ end
+
+ add_option('-I', '--[no-]ignore-dependencies',
+ 'Ignore dependency requirements while',
+ 'uninstalling') do |value, options|
+ options[:ignore] = value
+ 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',
+ 'Uninstall applicable executables without',
+ 'confirmation') do |value, options|
+ options[:executables] = value
+ end
+
+ add_option('-i', '--install-dir DIR',
+ 'Directory to uninstall gem from') do |value, options|
+ options[:install_dir] = File.expand_path(value)
+ end
+
+ add_option('-n', '--bindir DIR',
+ 'Directory to remove binaries from') do |value, options|
+ options[:bin_dir] = File.expand_path(value)
+ end
+
+ add_option('--[no-]user-install',
+ 'Uninstall from user\'s home directory',
+ 'in addition to GEM_HOME.') do |value, options|
+ options[:user_install] = value
+ end
+
+ add_option('--[no-]format-executable',
+ 'Assume executable names match Ruby\'s prefix and suffix.') do |value, options|
+ options[:format_executable] = value
+ end
+
+ add_option('--[no-]force',
+ 'Uninstall all versions of the named gems',
+ 'ignoring dependencies') do |value, options|
+ options[:force] = value
+ end
+
+ add_option('--[no-]abort-on-dependent',
+ 'Prevent uninstalling gems that are',
+ 'depended on by other gems.') do |value, options|
+ options[:abort_on_dependent] = value
+ end
+
+ add_version_option
+ add_platform_option
+
+ add_option('--vendor',
+ 'Uninstall gem from the vendor directory.',
+ 'Only for use by gem repackagers.') do |value, options|
+ unless Gem.vendor_dir then
+ raise OptionParser::InvalidOption.new 'your platform is not supported'
+ end
+
+ alert_warning 'Use your OS package manager to uninstall vendor gems'
+ options[:vendor] = true
+ options[:install_dir] = Gem.vendor_dir
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to uninstall"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}' --no-force " +
+ "--user-install"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The uninstall command removes a previously installed gem.
+
+RubyGems will ask for confirmation if you are attempting to uninstall a gem
+that is a dependency of an existing gem. You can use the
+--ignore-dependencies option to skip this check.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...]"
+ end
+
+ def execute
+ if options[:all] and not options[:args].empty? then
+ uninstall_specific
+ elsif options[:all] then
+ uninstall_all
+ else
+ uninstall_specific
+ end
+ end
+
+ def uninstall_all
+ specs = Gem::Specification.reject { |spec| spec.default_gem? }
+
+ specs.each do |spec|
+ options[:version] = spec.version
+
+ begin
+ Gem::Uninstaller.new(spec.name, options).uninstall
+ rescue Gem::InstallError
+ end
+ end
+
+ alert "Uninstalled all gems in #{options[:install_dir]}"
+ end
+
+ def uninstall_specific
+ 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
+
+ deps = deplist.strongly_connected_components.flatten.reverse
+
+ deps.map(&:name).uniq.each do |gem_name|
+ begin
+ Gem::Uninstaller.new(gem_name, options).uninstall
+ rescue Gem::GemNotInHomeException => e
+ spec = e.spec
+ alert("In order to remove #{spec.name}, please execute:\n" +
+ "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/unpack_command.rb b/jni/ruby/lib/rubygems/commands/unpack_command.rb
new file mode 100644
index 0000000..5a05ad0
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/unpack_command.rb
@@ -0,0 +1,182 @@
+require 'rubygems/command'
+require 'rubygems/installer'
+require 'rubygems/version_option'
+require 'rubygems/remote_fetcher'
+
+class Gem::Commands::UnpackCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ require 'fileutils'
+
+ super 'unpack', 'Unpack an installed gem to the current directory',
+ :version => Gem::Requirement.default,
+ :target => Dir.pwd
+
+ add_option('--target=DIR',
+ 'target directory for unpacking') do |value, options|
+ options[:target] = value
+ end
+
+ add_option('--spec', 'unpack the gem specification') do |value, options|
+ options[:spec] = true
+ end
+
+ add_version_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to unpack"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def description
+ <<-EOF
+The unpack command allows you to examine the contents of a gem or modify
+them to help diagnose a bug.
+
+You can add the contents of the unpacked gem to the load path using the
+RUBYLIB environment variable or -I:
+
+ $ gem unpack my_gem
+ Unpacked gem: '.../my_gem-1.0'
+ [edit my_gem-1.0/lib/my_gem.rb]
+ $ ruby -Imy_gem-1.0/lib -S other_program
+
+You can repackage an unpacked gem using the build command. See the build
+command help for an example.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ #--
+ # TODO: allow, e.g., 'gem unpack rake-0.3.1'. Find a general solution for
+ # this, so that it works for uninstall as well. (And check other commands
+ # at the same time.)
+
+ def execute
+ get_all_gem_names.each do |name|
+ dependency = Gem::Dependency.new name, options[:version]
+ path = get_path dependency
+
+ unless path then
+ alert_error "Gem '#{name}' not installed nor fetchable."
+ next
+ end
+
+ if @options[:spec] then
+ spec, metadata = get_metadata path
+
+ if metadata.nil? then
+ alert_error "--spec is unsupported on '#{name}' (old format gem)"
+ next
+ end
+
+ spec_file = File.basename spec.spec_file
+
+ open spec_file, 'w' do |io|
+ io.write metadata
+ end
+ else
+ basename = File.basename path, '.gem'
+ target_dir = File.expand_path basename, options[:target]
+
+ package = Gem::Package.new path
+ package.extract_files target_dir
+
+ say "Unpacked gem: '#{target_dir}'"
+ end
+ end
+ end
+
+ ##
+ #
+ # Find cached filename in Gem.path. Returns nil if the file cannot be found.
+ #
+ #--
+ # TODO: see comments in get_path() about general service.
+
+ def find_in_cache(filename)
+ Gem.path.each do |path|
+ this_path = File.join(path, "cache", filename)
+ return this_path if File.exist? this_path
+ end
+
+ return nil
+ end
+
+ ##
+ # Return the full path to the cached gem file matching the given
+ # name and version requirement. Returns 'nil' if no match.
+ #
+ # Example:
+ #
+ # get_path 'rake', '> 0.4' # "/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem"
+ # get_path 'rake', '< 0.1' # nil
+ # get_path 'rak' # nil (exact name required)
+ #--
+ # TODO: This should be refactored so that it's a general service. I don't
+ # think any of our existing classes are the right place though. Just maybe
+ # 'Cache'?
+ #
+ # TODO: It just uses Gem.dir for now. What's an easy way to get the list of
+ # source directories?
+
+ def get_path dependency
+ return dependency.name if dependency.name =~ /\.gem$/i
+
+ specs = dependency.matching_specs
+
+ selected = specs.max_by { |s| s.version }
+
+ return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless
+ selected
+
+ return unless dependency.name =~ /^#{selected.name}$/i
+
+ # We expect to find (basename).gem in the 'cache' directory. Furthermore,
+ # the name match must be exact (ignoring case).
+
+ path = find_in_cache File.basename selected.cache_file
+
+ return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless path
+
+ path
+ end
+
+ ##
+ # Extracts the Gem::Specification and raw metadata from the .gem file at
+ # +path+.
+ #--
+ # TODO move to Gem::Package as #raw_spec or something
+
+ def get_metadata path
+ format = Gem::Package.new path
+ spec = format.spec
+
+ metadata = nil
+
+ open path, Gem.binary_mode do |io|
+ tar = Gem::Package::TarReader.new io
+ tar.each_entry do |entry|
+ case entry.full_name
+ when 'metadata' then
+ metadata = entry.read
+ when 'metadata.gz' then
+ metadata = Gem.gunzip entry.read
+ end
+ end
+ end
+
+ return spec, metadata
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/update_command.rb b/jni/ruby/lib/rubygems/commands/update_command.rb
new file mode 100644
index 0000000..001dd77
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/update_command.rb
@@ -0,0 +1,277 @@
+require 'rubygems/command'
+require 'rubygems/command_manager'
+require 'rubygems/dependency_installer'
+require 'rubygems/install_update_options'
+require 'rubygems/local_remote_options'
+require 'rubygems/spec_fetcher'
+require 'rubygems/version_option'
+require 'rubygems/install_message' # must come before rdoc for messaging
+require 'rubygems/rdoc'
+
+class Gem::Commands::UpdateCommand < Gem::Command
+
+ include Gem::InstallUpdateOptions
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ attr_reader :installer # :nodoc:
+
+ attr_reader :updated # :nodoc:
+
+ def initialize
+ super 'update', 'Update installed gems to the latest version',
+ :document => %w[rdoc ri],
+ :force => false
+
+ add_install_update_options
+
+ OptionParser.accept Gem::Version do |value|
+ Gem::Version.new value
+
+ value
+ end
+
+ add_option('--system [VERSION]', Gem::Version,
+ 'Update the RubyGems system software') do |value, options|
+ value = true unless value
+
+ options[:system] = value
+ end
+
+ add_local_remote_options
+ add_platform_option
+ add_prerelease_option "as update targets"
+
+ @updated = []
+ @installer = nil
+ end
+
+ def arguments # :nodoc:
+ "REGEXP regexp to search for in gem name"
+ end
+
+ def defaults_str # :nodoc:
+ "--document --no-force --install-dir #{Gem.dir}"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The update command will update your gems to the latest version.
+
+The update command does not remove the previous version. Use the cleanup
+command to remove old versions.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} REGEXP [REGEXP ...]"
+ end
+
+ def check_latest_rubygems version # :nodoc:
+ if Gem.rubygems_version == version then
+ say "Latest version currently installed. Aborting."
+ terminate_interaction
+ end
+
+ options[:user_install] = false
+ end
+
+ def check_update_arguments # :nodoc:
+ unless options[:args].empty? then
+ alert_error "Gem names are not allowed with the --system option"
+ terminate_interaction 1
+ end
+ end
+
+ def execute
+ if options[:system] then
+ update_rubygems
+ return
+ end
+
+ say "Updating installed gems"
+
+ hig = highest_installed_gems
+
+ gems_to_update = which_to_update hig, options[:args].uniq
+
+ updated = update_gems gems_to_update
+
+ updated_names = updated.map { |spec| spec.name }
+ not_updated_names = options[:args].uniq - updated_names
+
+ if updated.empty? then
+ say "Nothing to update"
+ else
+ say "Gems updated: #{updated_names.join(' ')}"
+ say "Gems already up-to-date: #{not_updated_names.join(' ')}" unless not_updated_names.empty?
+ end
+ end
+
+ def fetch_remote_gems spec # :nodoc:
+ dependency = Gem::Dependency.new spec.name, "> #{spec.version}"
+ dependency.prerelease = options[:prerelease]
+
+ fetcher = Gem::SpecFetcher.fetcher
+
+ spec_tuples, errors = fetcher.search_for_dependency dependency
+
+ error = errors.find { |e| e.respond_to? :exception }
+
+ raise error if error
+
+ spec_tuples
+ end
+
+ def highest_installed_gems # :nodoc:
+ hig = {} # highest installed gems
+
+ Gem::Specification.each do |spec|
+ if hig[spec.name].nil? or hig[spec.name].version < spec.version then
+ hig[spec.name] = spec
+ end
+ end
+
+ hig
+ end
+
+ def highest_remote_version spec # :nodoc:
+ spec_tuples = fetch_remote_gems spec
+
+ matching_gems = spec_tuples.select do |g,_|
+ g.name == spec.name and g.match_platform?
+ end
+
+ highest_remote_gem = matching_gems.max_by { |g,_| g.version }
+
+ highest_remote_gem ||= [Gem::NameTuple.null]
+
+ highest_remote_gem.first.version
+ end
+
+ def install_rubygems version # :nodoc:
+ args = update_rubygems_arguments
+
+ update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}"
+
+ Dir.chdir update_dir do
+ say "Installing RubyGems #{version}"
+
+ # Make sure old rubygems isn't loaded
+ old = ENV["RUBYOPT"]
+ ENV.delete("RUBYOPT") if old
+ installed = system Gem.ruby, 'setup.rb', *args
+ say "RubyGems system software updated" if installed
+ ENV["RUBYOPT"] = old if old
+ end
+ end
+
+ def rubygems_target_version
+ version = options[:system]
+ update_latest = version == true
+
+ if update_latest then
+ version = Gem::Version.new Gem::VERSION
+ requirement = Gem::Requirement.new ">= #{Gem::VERSION}"
+ else
+ version = Gem::Version.new version
+ requirement = Gem::Requirement.new version
+ end
+
+ rubygems_update = Gem::Specification.new
+ rubygems_update.name = 'rubygems-update'
+ rubygems_update.version = version
+
+ hig = {
+ 'rubygems-update' => rubygems_update
+ }
+
+ gems_to_update = which_to_update hig, options[:args], :system
+ _, up_ver = gems_to_update.first
+
+ target = if update_latest then
+ up_ver
+ else
+ version
+ end
+
+ return target, requirement
+ end
+
+ def update_gem name, version = Gem::Requirement.default
+ return if @updated.any? { |spec| spec.name == name }
+
+ update_options = options.dup
+ update_options[:prerelease] = version.prerelease?
+
+ @installer = Gem::DependencyInstaller.new update_options
+
+ say "Updating #{name}"
+ begin
+ @installer.install name, Gem::Requirement.new(version)
+ rescue Gem::InstallError, Gem::DependencyError => e
+ alert_error "Error installing #{name}:\n\t#{e.message}"
+ end
+
+ @installer.installed_gems.each do |spec|
+ @updated << spec
+ end
+ end
+
+ def update_gems gems_to_update
+ gems_to_update.uniq.sort.each do |(name, version)|
+ update_gem name, version
+ end
+
+ @updated
+ end
+
+ ##
+ # Update RubyGems software to the latest version.
+
+ def update_rubygems
+ check_update_arguments
+
+ version, requirement = rubygems_target_version
+
+ check_latest_rubygems version
+
+ update_gem 'rubygems-update', version
+
+ installed_gems = Gem::Specification.find_all_by_name 'rubygems-update', requirement
+ version = installed_gems.last.version
+
+ install_rubygems version
+ end
+
+ def update_rubygems_arguments # :nodoc:
+ args = []
+ args << '--prefix' << Gem.prefix if Gem.prefix
+ # TODO use --document for >= 1.9 , --no-rdoc --no-ri < 1.9
+ 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 << '--previous-version' << Gem::VERSION if
+ options[:system] == true or
+ Gem::Version.new(options[:system]) >= Gem::Version.new(2)
+ args
+ end
+
+ def which_to_update highest_installed_gems, gem_names, system = false
+ result = []
+
+ highest_installed_gems.each do |l_name, l_spec|
+ next if not gem_names.empty? and
+ gem_names.none? { |name| name == l_spec.name }
+
+ highest_remote_ver = highest_remote_version l_spec
+
+ if system or (l_spec.version < highest_remote_ver) then
+ result << [l_spec.name, [l_spec.version, highest_remote_ver].max]
+ end
+ end
+
+ result
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/commands/which_command.rb b/jni/ruby/lib/rubygems/commands/which_command.rb
new file mode 100644
index 0000000..96eeb86
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/which_command.rb
@@ -0,0 +1,90 @@
+require 'rubygems/command'
+
+class Gem::Commands::WhichCommand < Gem::Command
+ def initialize
+ super 'which', 'Find the location of a library file you can require',
+ :search_gems_first => false, :show_all => false
+
+ add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options|
+ options[:show_all] = show_all
+ end
+
+ add_option '-g', '--[no-]gems-first',
+ 'search gems before non-gems' do |gems_first, options|
+ options[:search_gems_first] = gems_first
+ end
+ end
+
+ def arguments # :nodoc:
+ "FILE name of file to find"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-gems-first --no-all"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The which command is like the shell which command and shows you where
+the file you wish to require lives.
+
+You can use the which command to help determine why you are requiring a
+version you did not expect or to look at the content of a file you are
+requiring to see why it does not behave as you expect.
+ EOF
+ end
+
+ def execute
+ found = true
+
+ options[:args].each do |arg|
+ arg = arg.sub(/#{Regexp.union(*Gem.suffixes)}$/, '')
+ dirs = $LOAD_PATH
+
+ spec = Gem::Specification.find_by_path arg
+
+ if spec then
+ if options[:search_gems_first] then
+ dirs = spec.full_require_paths + $LOAD_PATH
+ else
+ dirs = $LOAD_PATH + spec.full_require_paths
+ end
+ end
+
+ # TODO: this is totally redundant and stupid
+ paths = find_paths arg, dirs
+
+ if paths.empty? then
+ alert_error "Can't find ruby library file or shared library #{arg}"
+
+ found &&= false
+ else
+ say paths
+ end
+ end
+
+ terminate_interaction 1 unless found
+ end
+
+ def find_paths(package_name, dirs)
+ result = []
+
+ dirs.each do |dir|
+ Gem.suffixes.each do |ext|
+ full_path = File.join dir, "#{package_name}#{ext}"
+ if File.exist? full_path and not File.directory? full_path then
+ result << full_path
+ return result unless options[:show_all]
+ end
+ end
+ end
+
+ result
+ end
+
+ def usage # :nodoc:
+ "#{program_name} FILE [FILE ...]"
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/commands/yank_command.rb b/jni/ruby/lib/rubygems/commands/yank_command.rb
new file mode 100644
index 0000000..3c7859e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/commands/yank_command.rb
@@ -0,0 +1,107 @@
+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:
+ <<-EOF
+The yank command removes a gem you pushed to a server from the server's
+index.
+
+Note that if you push a gem to rubygems.org the yank command does not
+prevent other people from downloading the gem via the download link.
+
+Once you have pushed a gem several downloads will happen automatically
+via the webhooks. If you accidentally pushed passwords or other sensitive
+data you will need to change them immediately and yank your gem.
+
+If you are yanking a gem due to intellectual property reasons contact
+http://help.rubygems.org for permanant removal. Be sure to mention this
+as the reason for the removal request.
+ EOF
+ 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', 'Remove a pushed gem from the index'
+
+ add_version_option("remove")
+ add_platform_option("remove")
+
+ add_option('--undo') do |value, options|
+ options[:undo] = true
+ end
+
+ add_key_option
+ end
+
+ def execute
+ sign_in
+
+ version = get_version_from_requirements(options[:version])
+ platform = get_platform_from_requirements(options)
+
+ if version then
+ if options[:undo] then
+ unyank_gem(version, platform)
+ else
+ yank_gem(version, platform)
+ end
+ else
+ say "A version argument is required: #{usage}"
+ terminate_interaction
+ end
+ end
+
+ def yank_gem(version, platform)
+ say "Yanking gem from #{self.host}..."
+ yank_api_request(:delete, version, platform, "api/v1/gems/yank")
+ end
+
+ def unyank_gem(version, platform)
+ say "Unyanking gem from #{host}..."
+ yank_api_request(:put, version, platform, "api/v1/gems/unyank")
+ end
+
+ private
+
+ def yank_api_request(method, version, platform, api)
+ 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
+
diff --git a/jni/ruby/lib/rubygems/compatibility.rb b/jni/ruby/lib/rubygems/compatibility.rb
new file mode 100644
index 0000000..6a00a19
--- /dev/null
+++ b/jni/ruby/lib/rubygems/compatibility.rb
@@ -0,0 +1,59 @@
+# :stopdoc:
+
+#--
+# 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 path = $LOADED_FEATURES.find {|n| n.end_with? '/rubygems.rb'} then
+ 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
+
+ # TODO remove at RubyGems 3
+
+ RbConfigPriorities = %w[
+ MAJOR
+ MINOR
+ TEENY
+ 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| # TODO remove at RubyGems 3
+ 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
diff --git a/jni/ruby/lib/rubygems/config_file.rb b/jni/ruby/lib/rubygems/config_file.rb
new file mode 100644
index 0000000..1bdc79a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/config_file.rb
@@ -0,0 +1,483 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/user_interaction'
+require 'rbconfig'
+
+##
+# Gem::ConfigFile RubyGems options and gem command options from gemrc.
+#
+# gemrc is a YAML file that uses strings to match gem command arguments and
+# symbols to match RubyGems options.
+#
+# Gem command arguments use a String key that matches the command name and
+# allow you to specify default arguments:
+#
+# install: --no-rdoc --no-ri
+# update: --no-rdoc --no-ri
+#
+# You can use <tt>gem:</tt> to set default arguments for all commands.
+#
+# RubyGems options use symbol keys. Valid options are:
+#
+# +:backtrace+:: See #backtrace
+# +:sources+:: Sets Gem::sources
+# +:verbose+:: See #verbose
+#
+# 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
+
+ include Gem::UserInteraction
+
+ DEFAULT_BACKTRACE = false
+ DEFAULT_BULK_THRESHOLD = 1000
+ DEFAULT_VERBOSITY = true
+ DEFAULT_UPDATE_SOURCES = true
+
+ ##
+ # For Ruby packagers to set configuration defaults. Set in
+ # rubygems/defaults/operating_system.rb
+
+ OPERATING_SYSTEM_DEFAULTS = {}
+
+ ##
+ # For Ruby implementers to set configuration defaults. Set in
+ # rubygems/defaults/#{RUBY_ENGINE}.rb
+
+ PLATFORM_DEFAULTS = {}
+
+ # :stopdoc:
+
+ SYSTEM_CONFIG_PATH =
+ begin
+ require "etc"
+ Etc.sysconfdir
+ rescue LoadError, NoMethodError
+ begin
+ # TODO: remove after we drop 1.8.7 and 1.9.1
+ require 'Win32API'
+
+ CSIDL_COMMON_APPDATA = 0x0023
+ path = 0.chr * 260
+ if RUBY_VERSION > '1.9' then
+ SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'PLPLP',
+ 'L', :stdcall
+ SHGetFolderPath.call nil, CSIDL_COMMON_APPDATA, nil, 1, path
+ else
+ SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'LLLLP',
+ 'L'
+ SHGetFolderPath.call 0, CSIDL_COMMON_APPDATA, 0, 1, path
+ end
+
+ path.strip
+ rescue LoadError
+ RbConfig::CONFIG["sysconfdir"] || "/etc"
+ end
+ end
+
+ # :startdoc:
+
+ SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, 'gemrc'
+
+ ##
+ # List of arguments supplied to the config file object.
+
+ attr_reader :args
+
+ ##
+ # Where to look for gems (deprecated)
+
+ attr_accessor :path
+
+ ##
+ # Where to install gems (deprecated)
+
+ attr_accessor :home
+
+ ##
+ # True if we print backtraces on errors.
+
+ attr_writer :backtrace
+
+ ##
+ # Bulk threshold value. If the number of missing gems are above this
+ # threshold value, then a bulk download technique is used. (deprecated)
+
+ attr_accessor :bulk_threshold
+
+ ##
+ # Verbose level of output:
+ # * false -- No output
+ # * true -- Normal output
+ # * :loud -- Extra output
+
+ attr_accessor :verbose
+
+ ##
+ # True if we want to update the SourceInfoCache every time, false otherwise
+
+ attr_accessor :update_sources
+
+ ##
+ # 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
+
+ attr_reader :ssl_verify_mode
+
+ ##
+ # Path name of directory or file of openssl CA certificate, used for remote
+ # https connection
+
+ attr_accessor :ssl_ca_cert
+
+ ##
+ # Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
+
+ attr_reader :ssl_client_cert
+
+ ##
+ # Create the config file object. +args+ is the list of arguments
+ # from the command line.
+ #
+ # The following command line options are handled early here rather
+ # than later at the time most command options are processed.
+ #
+ # <tt>--config-file</tt>, <tt>--config-file==NAME</tt>::
+ # Obviously these need to be handled by the ConfigFile object to ensure we
+ # get the right config file.
+ #
+ # <tt>--backtrace</tt>::
+ # Backtrace needs to be turned on early so that errors before normal
+ # option parsing can be properly handled.
+ #
+ # <tt>--debug</tt>::
+ # Enable Ruby level debug messages. Handled early for the same reason as
+ # --backtrace.
+ #--
+ # TODO: parse options upstream, pass in options directly
+
+ def initialize(args)
+ @config_file_name = nil
+ need_config_file_name = false
+
+ arg_list = []
+
+ args.each do |arg|
+ if need_config_file_name then
+ @config_file_name = arg
+ need_config_file_name = false
+ elsif arg =~ /^--config-file=(.*)/ then
+ @config_file_name = $1
+ elsif arg =~ /^--config-file$/ then
+ need_config_file_name = true
+ else
+ arg_list << arg
+ end
+ end
+
+ @backtrace = DEFAULT_BACKTRACE
+ @bulk_threshold = DEFAULT_BULK_THRESHOLD
+ @verbose = DEFAULT_VERBOSITY
+ @update_sources = DEFAULT_UPDATE_SOURCES
+
+ operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
+ platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
+ system_config = load_file SYSTEM_WIDE_CONFIG_FILE
+ 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 = @hash.merge system_config
+ @hash = @hash.merge user_config
+ @hash = @hash.merge environment_config
+
+ # HACK these override command-line args, which is bad
+ @backtrace = @hash[:backtrace] if @hash.key? :backtrace
+ @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
+ @home = @hash[:gemhome] if @hash.key? :gemhome
+ @path = @hash[:gempath] if @hash.key? :gempath
+ @update_sources = @hash[:update_sources] if @hash.key? :update_sources
+ @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_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
+ @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
+
+ @api_keys = nil
+ @rubygems_api_key = nil
+
+ Gem.sources = @hash[:sources] if @hash.key? :sources
+ handle_arguments arg_list
+ end
+
+ ##
+ # Hash of RubyGems.org and alternate API keys
+
+ def api_keys
+ load_api_keys unless @api_keys
+
+ @api_keys
+ end
+
+ ##
+ # Checks the permissions of the credentials file. If they are not 0600 an
+ # error message is displayed and RubyGems aborts.
+
+ def check_credentials_permissions
+ return if Gem.win_platform? # windows doesn't write 0600 as 0600
+ return unless File.exist? credentials_path
+
+ existing_permissions = File.stat(credentials_path).mode & 0777
+
+ return if existing_permissions == 0600
+
+ alert_error <<-ERROR
+Your gem push credentials file located at:
+
+\t#{credentials_path}
+
+has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required.
+
+To fix this error run:
+
+\tchmod 0600 #{credentials_path}
+
+You should reset your credentials at:
+
+\thttps://rubygems.org/profile/edit
+
+if you believe they were disclosed to a third party.
+ ERROR
+
+ terminate_interaction 1
+ end
+
+ ##
+ # Location of RubyGems.org credentials
+
+ def credentials_path
+ File.join Gem.user_home, '.gem', 'credentials'
+ end
+
+ def load_api_keys
+ check_credentials_permissions
+
+ @api_keys = if File.exist? credentials_path then
+ load_file(credentials_path)
+ else
+ @hash
+ end
+
+ if @api_keys.key? :rubygems_api_key then
+ @rubygems_api_key = @api_keys[:rubygems_api_key]
+ @api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless
+ @api_keys.key? :rubygems
+ end
+ end
+
+ ##
+ # Returns the RubyGems.org API key
+
+ def rubygems_api_key
+ load_api_keys unless @rubygems_api_key
+
+ @rubygems_api_key
+ end
+
+ ##
+ # Sets the RubyGems.org API key to +api_key+
+
+ def rubygems_api_key= api_key
+ check_credentials_permissions
+
+ config = load_file(credentials_path).merge(:rubygems_api_key => api_key)
+
+ dirname = File.dirname credentials_path
+ Dir.mkdir(dirname) unless File.exist? dirname
+
+ Gem.load_yaml
+
+ permissions = 0600 & (~File.umask)
+ File.open(credentials_path, 'w', permissions) do |f|
+ f.write config.to_yaml
+ end
+
+ @rubygems_api_key = api_key
+ end
+
+ YAMLErrors = [ArgumentError]
+ YAMLErrors << Psych::SyntaxError if defined?(Psych::SyntaxError)
+
+ def load_file(filename)
+ Gem.load_yaml
+
+ return {} unless filename and File.exist? filename
+
+ begin
+ content = YAML.load(File.read(filename))
+ unless content.kind_of? Hash
+ warn "Failed to load #{filename} because it doesn't contain valid YAML hash"
+ return {}
+ end
+ return content
+ rescue *YAMLErrors => e
+ warn "Failed to load #{filename}, #{e}"
+ rescue Errno::EACCES
+ warn "Failed to load #{filename} due to permissions problem."
+ end
+
+ {}
+ end
+
+ # True if the backtrace option has been specified, or debug is on.
+ def backtrace
+ @backtrace or $DEBUG
+ end
+
+ # The name of the configuration file.
+ def config_file_name
+ @config_file_name || Gem.config_file
+ end
+
+ # Delegates to @hash
+ def each(&block)
+ hash = @hash.dup
+ hash.delete :update_sources
+ hash.delete :verbose
+ hash.delete :backtrace
+ hash.delete :bulk_threshold
+
+ yield :update_sources, @update_sources
+ yield :verbose, @verbose
+ yield :backtrace, @backtrace
+ yield :bulk_threshold, @bulk_threshold
+
+ yield 'config_file_name', @config_file_name if @config_file_name
+
+ hash.each(&block)
+ end
+
+ # Handle the command arguments.
+ def handle_arguments(arg_list)
+ @args = []
+
+ arg_list.each do |arg|
+ case arg
+ when /^--(backtrace|traceback)$/ then
+ @backtrace = true
+ when /^--debug$/ then
+ $DEBUG = true
+
+ warn 'NOTE: Debugging mode prints all exceptions even when rescued'
+ else
+ @args << arg
+ end
+ end
+ end
+
+ # Really verbose mode gives you extra output.
+ def really_verbose
+ case verbose
+ when true, false, nil then
+ false
+ else
+ true
+ end
+ end
+
+ # to_yaml only overwrites things you can't override on the command line.
+ def to_yaml # :nodoc:
+ yaml_hash = {}
+ yaml_hash[:backtrace] = if @hash.key?(:backtrace)
+ @hash[:backtrace]
+ else
+ DEFAULT_BACKTRACE
+ end
+
+ yaml_hash[:bulk_threshold] = if @hash.key?(:bulk_threshold)
+ @hash[:bulk_threshold]
+ else
+ DEFAULT_BULK_THRESHOLD
+ 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
+
+ yaml_hash[:ssl_verify_mode] =
+ @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
+
+ yaml_hash[:ssl_ca_cert] =
+ @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
+
+ yaml_hash[:ssl_client_cert] =
+ @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
+
+ keys = yaml_hash.keys.map { |key| key.to_s }
+ keys << 'debug'
+ re = Regexp.union(*keys)
+
+ @hash.each do |key, value|
+ key = key.to_s
+ next if key =~ re
+ yaml_hash[key.to_s] = value
+ end
+
+ yaml_hash.to_yaml
+ end
+
+ # Writes out this config file, replacing its source.
+ def write
+ open config_file_name, 'w' do |io|
+ io.write to_yaml
+ end
+ end
+
+ # Return the configuration information for +key+.
+ def [](key)
+ @hash[key.to_s]
+ end
+
+ # Set configuration option +key+ to +value+.
+ def []=(key, value)
+ @hash[key.to_s] = value
+ end
+
+ def ==(other) # :nodoc:
+ self.class === other and
+ @backtrace == other.backtrace and
+ @bulk_threshold == other.bulk_threshold and
+ @verbose == other.verbose and
+ @update_sources == other.update_sources and
+ @hash == other.hash
+ end
+
+ attr_reader :hash
+ protected :hash
+end
diff --git a/jni/ruby/lib/rubygems/core_ext/kernel_gem.rb b/jni/ruby/lib/rubygems/core_ext/kernel_gem.rb
new file mode 100644
index 0000000..61e77fe
--- /dev/null
+++ b/jni/ruby/lib/rubygems/core_ext/kernel_gem.rb
@@ -0,0 +1,73 @@
+##
+# RubyGems adds the #gem method to allow activation of specific gem versions
+# and overrides the #require method on Kernel to make gems appear as if they
+# live on the <code>$LOAD_PATH</code>. See the documentation of these methods
+# for further detail.
+
+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).
+ #
+ # Kernel#gem only loads prerelease versions when prerelease +requirements+
+ # are given:
+ #
+ # gem 'rake', '>= 1.1.a', '< 2'
+ #
+ # 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
+
+ dep = Gem::Dependency.new(gem_name, *requirements)
+
+ loaded = Gem.loaded_specs[gem_name]
+
+ return false if loaded && dep.matches_spec?(loaded)
+
+ spec = dep.to_spec
+
+ Gem::LOADED_SPECS_MUTEX.synchronize {
+ spec.activate
+ } if spec
+ end
+
+ private :gem
+
+end
diff --git a/jni/ruby/lib/rubygems/core_ext/kernel_require.rb b/jni/ruby/lib/rubygems/core_ext/kernel_require.rb
new file mode 100755
index 0000000..8f2cdde
--- /dev/null
+++ b/jni/ruby/lib/rubygems/core_ext/kernel_require.rb
@@ -0,0 +1,139 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'monitor'
+
+module Kernel
+
+ RUBYGEMS_ACTIVATION_MONITOR = Monitor.new # :nodoc:
+
+ 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
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
+ path = path.to_path if path.respond_to? :to_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
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ 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.stubs.find { |s|
+ s.activated? and s.contains_requirable_file? path
+ }
+
+ begin
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(spec.to_fullpath(path) || path)
+ end 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
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ 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
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ raise le
+ end
+
+ valid.activate
+ end
+
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
+ rescue LoadError => load_error
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
+ if load_error.message.start_with?("Could not find") or
+ (load_error.message.end_with?(path) and Gem.try_activate(path)) then
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
+ else
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ end
+
+ raise load_error
+ end
+
+ private :require
+
+end
+
diff --git a/jni/ruby/lib/rubygems/defaults.rb b/jni/ruby/lib/rubygems/defaults.rb
new file mode 100644
index 0000000..55ca080
--- /dev/null
+++ b/jni/ruby/lib/rubygems/defaults.rb
@@ -0,0 +1,177 @@
+module Gem
+ DEFAULT_HOST = "https://rubygems.org"
+
+ @post_install_hooks ||= []
+ @done_installing_hooks ||= []
+ @post_uninstall_hooks ||= []
+ @pre_uninstall_hooks ||= []
+ @pre_install_hooks ||= []
+
+ ##
+ # An Array of the default sources that come with RubyGems
+
+ def self.default_sources
+ %w[https://rubygems.org/]
+ end
+
+ ##
+ # Default spec directory path to be used if an alternate value is not
+ # specified in the environment
+
+ def self.default_spec_cache_dir
+ File.join Gem.user_home, '.gem', 'specs'
+ end
+
+ ##
+ # Default home directory path to be used if an alternate value is not
+ # specified in the environment
+
+ def self.default_dir
+ path = if defined? RUBY_FRAMEWORK_VERSION then
+ [
+ File.dirname(RbConfig::CONFIG['sitedir']),
+ 'Gems',
+ RbConfig::CONFIG['ruby_version']
+ ]
+ elsif RbConfig::CONFIG['rubylibprefix'] then
+ [
+ RbConfig::CONFIG['rubylibprefix'],
+ 'gems',
+ RbConfig::CONFIG['ruby_version']
+ ]
+ else
+ [
+ RbConfig::CONFIG['libdir'],
+ ruby_engine,
+ 'gems',
+ RbConfig::CONFIG['ruby_version']
+ ]
+ end
+
+ @default_dir ||= File.join(*path)
+ end
+
+ ##
+ # Returns binary extensions dir for specified RubyGems base dir or nil
+ # if such directory cannot be determined.
+ #
+ # By default, the binary extensions are located side by side with their
+ # Ruby counterparts, therefore nil is returned
+
+ def self.default_ext_dir_for base_dir
+ nil
+ end
+
+ ##
+ # Paths where RubyGems' .rb files and bin files are installed
+
+ def self.default_rubygems_dirs
+ nil # default to standard layout
+ end
+
+ ##
+ # Path for gems in the user's home directory
+
+ def self.user_dir
+ parts = [Gem.user_home, '.gem', ruby_engine]
+ parts << RbConfig::CONFIG['ruby_version'] unless RbConfig::CONFIG['ruby_version'].empty?
+ File.join parts
+ end
+
+ ##
+ # How String Gem paths should be split. Overridable for esoteric platforms.
+
+ def self.path_separator
+ File::PATH_SEPARATOR
+ end
+
+ ##
+ # Default gem load path
+
+ def self.default_path
+ path = []
+ path << user_dir if user_home && File.exist?(user_home)
+ path << default_dir
+ path << vendor_dir if vendor_dir and File.directory? vendor_dir
+ path
+ end
+
+ ##
+ # Deduce Ruby's --program-prefix and --program-suffix from its install name
+
+ def self.default_exec_format
+ exec_format = RbConfig::CONFIG['ruby_install_name'].sub('ruby', '%s') rescue '%s'
+
+ unless exec_format =~ /%s/ then
+ raise Gem::Exception,
+ "[BUG] invalid exec_format #{exec_format.inspect}, no %s"
+ end
+
+ exec_format
+ end
+
+ ##
+ # The default directory for binaries
+
+ def self.default_bindir
+ if defined? RUBY_FRAMEWORK_VERSION then # mac framework support
+ '/usr/bin'
+ else # generic install
+ RbConfig::CONFIG['bindir']
+ end
+ end
+
+ ##
+ # A wrapper around RUBY_ENGINE const that may not be defined
+
+ def self.ruby_engine
+ if defined? RUBY_ENGINE then
+ RUBY_ENGINE
+ else
+ 'ruby'
+ end
+ end
+
+ ##
+ # The default signing key path
+
+ def self.default_key_path
+ File.join Gem.user_home, ".gem", "gem-private_key.pem"
+ end
+
+ ##
+ # The default signing certificate chain path
+
+ def self.default_cert_path
+ File.join Gem.user_home, ".gem", "gem-public_cert.pem"
+ end
+
+ ##
+ # Whether to expect full paths in default gems - true for non-MRI
+ # ruby implementations
+ def self.default_gems_use_full_paths?
+ ruby_engine != 'ruby'
+ end
+
+ ##
+ # Install extensions into lib as well as into the extension directory.
+
+ def self.install_extension_in_lib # :nodoc:
+ true
+ end
+
+ ##
+ # Directory where vendor gems are installed.
+
+ def self.vendor_dir # :nodoc:
+ if vendor_dir = ENV['GEM_VENDOR'] then
+ return vendor_dir.dup
+ end
+
+ return nil unless RbConfig::CONFIG.key? 'vendordir'
+
+ File.join RbConfig::CONFIG['vendordir'], 'gems',
+ RbConfig::CONFIG['ruby_version']
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/dependency.rb b/jni/ruby/lib/rubygems/dependency.rb
new file mode 100644
index 0000000..5924d2f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/dependency.rb
@@ -0,0 +1,334 @@
+##
+# The Dependency class holds a Gem name and a Gem::Requirement.
+
+require "rubygems/requirement"
+
+class Gem::Dependency
+
+ ##
+ # Valid dependency types.
+ #--
+ # When this list is updated, be sure to change
+ # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well.
+ #
+ # REFACTOR: This type of constant, TYPES, indicates we might want
+ # two classes, used via inheritance or duck typing.
+
+ TYPES = [
+ :development,
+ :runtime,
+ ]
+
+ ##
+ # Dependency name or regular expression.
+
+ attr_accessor :name
+
+ ##
+ # Allows you to force this dependency to be a prerelease.
+
+ attr_writer :prerelease
+
+ ##
+ # Constructs a dependency with +name+ and +requirements+. The last
+ # argument can optionally be the dependency type, which defaults to
+ # <tt>:runtime</tt>.
+
+ def initialize name, *requirements
+ case name
+ when String then # ok
+ when Regexp then
+ msg = ["NOTE: Dependency.new w/ a regexp is deprecated.",
+ "Dependency.new called from #{Gem.location_of_caller.join(":")}"]
+ warn msg.join("\n") unless Gem::Deprecate.skip
+ else
+ raise ArgumentError,
+ "dependency name must be a String, was #{name.inspect}"
+ end
+
+ type = Symbol === requirements.last ? requirements.pop : :runtime
+ requirements = requirements.first if 1 == requirements.length # unpack
+
+ unless TYPES.include? type
+ raise ArgumentError, "Valid types are #{TYPES.inspect}, " +
+ "not #{type.inspect}"
+ end
+
+ @name = name
+ @requirement = Gem::Requirement.create requirements
+ @type = type
+ @prerelease = false
+
+ # This is for Marshal backwards compatibility. See the comments in
+ # +requirement+ for the dirty details.
+
+ @version_requirements = @requirement
+ end
+
+ ##
+ # A dependency's hash is the XOR of the hashes of +name+, +type+,
+ # and +requirement+.
+
+ def hash # :nodoc:
+ name.hash ^ type.hash ^ requirement.hash
+ end
+
+ def inspect # :nodoc:
+ if prerelease? then
+ "<%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
+
+ ##
+ # Does this dependency require a prerelease?
+
+ def prerelease?
+ @prerelease || requirement.prerelease?
+ end
+
+ ##
+ # Is this dependency simply asking for the latest version
+ # of a gem?
+
+ def latest_version?
+ @requirement.none?
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 1, 'Gem::Dependency.new(', ')' do
+ q.pp name
+ q.text ','
+ q.breakable
+
+ q.pp requirement
+
+ q.text ','
+ q.breakable
+
+ q.pp type
+ end
+ end
+
+ ##
+ # What does this dependency require?
+
+ def requirement
+ return @requirement if defined?(@requirement) and @requirement
+
+ # @version_requirements and @version_requirement are legacy ivar
+ # names, and supported here because older gems need to keep
+ # working and Dependency doesn't implement marshal_dump and
+ # marshal_load. In a happier world, this would be an
+ # attr_accessor. The horrifying instance_variable_get you see
+ # below is also the legacy of some old restructurings.
+ #
+ # Note also that because of backwards compatibility (loading new
+ # gems in an old RubyGems installation), we can't add explicit
+ # marshaling to this class until we want to make a big
+ # break. Maybe 2.0.
+ #
+ # Children, define explicit marshal and unmarshal behavior for
+ # public classes. Marshal formats are part of your public API.
+
+ # REFACTOR: See above
+
+ if defined?(@version_requirement) && @version_requirement
+ version = @version_requirement.instance_variable_get :@version
+ @version_requirement = nil
+ @version_requirements = Gem::Requirement.new version
+ end
+
+ @requirement = @version_requirements if defined?(@version_requirements)
+ end
+
+ def requirements_list
+ requirement.as_list
+ end
+
+ def to_s # :nodoc:
+ if type != :runtime then
+ "#{name} (#{requirement}, #{type})"
+ else
+ "#{name} (#{requirement})"
+ end
+ end
+
+ ##
+ # Dependency type.
+
+ def type
+ @type ||= :runtime
+ end
+
+ def == other # :nodoc:
+ Gem::Dependency === other &&
+ self.name == other.name &&
+ self.type == other.type &&
+ self.requirement == other.requirement
+ end
+
+ ##
+ # Dependencies are ordered by name.
+
+ def <=> other
+ self.name <=> other.name
+ end
+
+ ##
+ # Uses this dependency as a pattern to compare to +other+. This
+ # dependency will match if the name matches the other's name, and
+ # other has only an equal version requirement that satisfies this
+ # dependency.
+
+ def =~ other
+ unless Gem::Dependency === other
+ return unless other.respond_to?(:name) && other.respond_to?(:version)
+ other = Gem::Dependency.new other.name, other.version
+ end
+
+ return false unless name === other.name
+
+ reqs = other.requirement.requirements
+
+ return false unless reqs.length == 1
+ return false unless reqs.first.first == '='
+
+ version = reqs.first.last
+
+ requirement.satisfied_by? version
+ end
+
+ alias === =~
+
+ ##
+ # :call-seq:
+ # dep.match? name => true or false
+ # dep.match? name, version => true or false
+ # dep.match? spec => true or false
+ #
+ # Does this dependency match the specification described by +name+ and
+ # +version+ or match +spec+?
+ #
+ # NOTE: Unlike #matches_spec? this method does not return true when the
+ # version is a prerelease version unless this is a prerelease dependency.
+
+ def match? obj, version=nil, allow_prerelease=false
+ if !version
+ name = obj.name
+ version = obj.version
+ else
+ name = obj
+ end
+
+ return false unless self.name === name
+
+ version = Gem::Version.new version
+
+ return true if requirement.none? and not version.prerelease?
+ return false if version.prerelease? and
+ not allow_prerelease and
+ not prerelease?
+
+ requirement.satisfied_by? version
+ end
+
+ ##
+ # Does this dependency match +spec+?
+ #
+ # NOTE: This is not a convenience method. Unlike #match? this method
+ # returns true when +spec+ is a prerelease version even if this dependency
+ # is not a prerelease dependency.
+
+ def matches_spec? spec
+ return false unless name === spec.name
+ return true if requirement.none?
+
+ requirement.satisfied_by?(spec.version)
+ end
+
+ ##
+ # Merges the requirements of +other+ into this dependency
+
+ def merge other
+ unless name == other.name then
+ raise ArgumentError,
+ "#{self} and #{other} have different names"
+ end
+
+ default = Gem::Requirement.default
+ self_req = self.requirement
+ other_req = other.requirement
+
+ return self.class.new name, self_req if other_req == default
+ return self.class.new name, other_req if self_req == default
+
+ self.class.new name, self_req.as_list.concat(other_req.as_list)
+ end
+
+ def matching_specs platform_only = false
+ matches = Gem::Specification.stubs.find_all { |spec|
+ self.name === spec.name and # TODO: == instead of ===
+ requirement.satisfied_by? spec.version
+ }.map(&:to_spec)
+
+ if platform_only
+ matches.reject! { |spec|
+ not Gem::Platform.match spec.platform
+ }
+ end
+
+ matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
+ end
+
+ ##
+ # True if the dependency will not always match the latest version.
+
+ def specific?
+ @requirement.specific?
+ end
+
+ def to_specs
+ matches = matching_specs true
+
+ # TODO: check Gem.activated_spec[self.name] in case matches falls outside
+
+ if matches.empty? then
+ specs = Gem::Specification.find_all { |s|
+ s.name == name
+ }.map { |x| x.full_name }
+
+ if specs.empty?
+ total = Gem::Specification.to_a.size
+ msg = "Could not find '#{name}' (#{requirement}) among #{total} total gem(s)\n"
+ else
+ msg = "Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]\n"
+ end
+ msg << "Checked in 'GEM_PATH=#{Gem.path.join(File::PATH_SEPARATOR)}', execute `gem env` for more information"
+
+ error = Gem::LoadError.new(msg)
+ error.name = self.name
+ error.requirement = self.requirement
+ raise error
+ end
+
+ # TODO: any other resolver validations should go here
+
+ matches
+ end
+
+ def to_spec
+ matches = self.to_specs
+
+ active = matches.find { |spec| spec.activated? }
+
+ return active if active
+
+ matches.delete_if { |spec| spec.version.prerelease? } unless prerelease?
+
+ matches.last
+ end
+end
diff --git a/jni/ruby/lib/rubygems/dependency_installer.rb b/jni/ruby/lib/rubygems/dependency_installer.rb
new file mode 100644
index 0000000..039d046
--- /dev/null
+++ b/jni/ruby/lib/rubygems/dependency_installer.rb
@@ -0,0 +1,480 @@
+require 'rubygems'
+require 'rubygems/dependency_list'
+require 'rubygems/package'
+require 'rubygems/installer'
+require 'rubygems/spec_fetcher'
+require 'rubygems/user_interaction'
+require 'rubygems/source'
+require 'rubygems/available_set'
+
+##
+# Installs a gem along with all its dependencies from local and remote gems.
+
+class Gem::DependencyInstaller
+
+ include Gem::UserInteraction
+
+ DEFAULT_OPTIONS = { # :nodoc:
+ :env_shebang => false,
+ :document => %w[ri],
+ :domain => :both, # HACK dup
+ :force => false,
+ :format_executable => false, # HACK dup
+ :ignore_dependencies => false,
+ :prerelease => false,
+ :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
+ :wrappers => true,
+ :build_args => nil,
+ :build_docs_in_background => false,
+ :install_as_default => false
+ }.freeze
+
+ ##
+ # Documentation types. For use by the Gem.done_installing hook
+
+ attr_reader :document
+
+ ##
+ # Errors from SpecFetcher while searching for remote specifications
+
+ attr_reader :errors
+
+ ##
+ #--
+ # TODO remove, no longer used
+
+ attr_reader :gems_to_install # :nodoc:
+
+ ##
+ # List of gems installed by #install in alphabetic order
+
+ attr_reader :installed_gems
+
+ ##
+ # Creates a new installer instance.
+ #
+ # Options are:
+ # :cache_dir:: Alternate repository path to store .gem files in.
+ # :domain:: :local, :remote, or :both. :local only searches gems in the
+ # current directory. :remote searches only gems in Gem::sources.
+ # :both searches both.
+ # :env_shebang:: See Gem::Installer::new.
+ # :force:: See Gem::Installer#install.
+ # :format_executable:: See Gem::Installer#initialize.
+ # :ignore_dependencies:: Don't install any dependencies.
+ # :install_dir:: See Gem::Installer#install.
+ # :prerelease:: Allow prerelease versions. See #install.
+ # :security_policy:: See Gem::Installer::new and Gem::Security.
+ # :user_install:: See Gem::Installer.new
+ # :wrappers:: See Gem::Installer::new
+ # :build_args:: See Gem::Installer::new
+
+ def initialize options = {}
+ @only_install_dir = !!options[:install_dir]
+ @install_dir = options[:install_dir] || Gem.dir
+ @build_root = options[:build_root]
+
+ options = DEFAULT_OPTIONS.merge options
+
+ @bin_dir = options[:bin_dir]
+ @dev_shallow = options[:dev_shallow]
+ @development = options[:development]
+ @document = options[:document]
+ @domain = options[:domain]
+ @env_shebang = options[:env_shebang]
+ @force = options[:force]
+ @format_executable = options[:format_executable]
+ @ignore_dependencies = options[:ignore_dependencies]
+ @prerelease = options[:prerelease]
+ @security_policy = options[:security_policy]
+ @user_install = options[:user_install]
+ @wrappers = options[:wrappers]
+ @build_args = options[:build_args]
+ @build_docs_in_background = options[:build_docs_in_background]
+ @install_as_default = options[:install_as_default]
+
+ # Indicates that we should not try to update any deps unless
+ # we absolutely must.
+ @minimal_deps = options[:minimal_deps]
+
+ @available = nil
+ @installed_gems = []
+ @toplevel_specs = nil
+
+ @cache_dir = options[:cache_dir] || @install_dir
+
+ @errors = []
+ end
+
+ ##
+ #--
+ # TODO remove, no longer used
+
+ def add_found_dependencies to_do, dependency_list # :nodoc:
+ seen = {}
+ dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
+
+ until to_do.empty? do
+ spec = to_do.shift
+
+ # HACK why is spec nil?
+ next if spec.nil? or seen[spec.name]
+ seen[spec.name] = true
+
+ deps = spec.runtime_dependencies
+
+ 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|
+ dependencies[dep.name] = dependencies[dep.name].merge dep
+
+ if @minimal_deps
+ next if Gem::Specification.any? do |installed_spec|
+ dep.name == installed_spec.name and
+ dep.requirement.satisfied_by? installed_spec.version
+ end
+ end
+
+ results = find_gems_with_sources(dep)
+
+ results.sorted.each do |t|
+ to_do.push t.spec
+ end
+
+ results.remove_installed! dep
+
+ @available << results
+ results.inject_into_list dependency_list
+ end
+ end
+
+ dependency_list.remove_specs_unsatisfied_by dependencies
+ end
+
+ ##
+ # Creates an AvailableSet to install from based on +dep_or_name+ and
+ # +version+
+
+ def available_set_for dep_or_name, version # :nodoc:
+ if String === dep_or_name then
+ find_spec_by_name_and_version dep_or_name, version, @prerelease
+ else
+ dep = dep_or_name.dup
+ dep.prerelease = @prerelease
+ @available = find_gems_with_sources dep
+ end
+
+ @available.pick_best!
+ end
+
+ ##
+ # 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
+ # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
+ # sources. Gems are sorted with newer gems preferred over older gems, and
+ # local gems preferred over remote gems.
+
+ def find_gems_with_sources dep, best_only=false # :nodoc:
+ set = Gem::AvailableSet.new
+
+ if consider_local?
+ sl = Gem::Source::Local.new
+
+ if spec = sl.find_gem(dep.name)
+ if dep.matches_spec? spec
+ set.add spec, sl
+ end
+ end
+ end
+
+ if consider_remote?
+ begin
+ # TODO this is pulled from #spec_for_dependency to allow
+ # us to filter tuples before fetching specs.
+ #
+ tuples, errors = Gem::SpecFetcher.fetcher.search_for_dependency dep
+
+ if best_only && !tuples.empty?
+ tuples.sort! { |a,b| b[0].version <=> a[0].version }
+ tuples = [tuples.first]
+ end
+
+ 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
+
+ if @errors
+ @errors += errors
+ else
+ @errors = errors
+ end
+
+ set << specs
+
+ 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.
+ verbose do
+ "Error fetching remote data:\t\t#{e.message}\n" \
+ "Falling back to local-only install"
+ end
+ @domain = :local
+ end
+ end
+
+ set
+ end
+
+ ##
+ # Finds a spec and the source_uri it came from for gem +gem_name+ and
+ # +version+. Returns an Array of specs and sources required for
+ # installation of the gem.
+
+ def find_spec_by_name_and_version gem_name,
+ version = Gem::Requirement.default,
+ prerelease = false
+ set = Gem::AvailableSet.new
+
+ if consider_local?
+ if gem_name =~ /\.gem$/ and File.file? gem_name then
+ src = Gem::Source::SpecificFile.new(gem_name)
+ set.add src.spec, src
+ elsif gem_name =~ /\.gem$/ then
+ Dir[gem_name].each do |name|
+ begin
+ src = Gem::Source::SpecificFile.new name
+ set.add src.spec, src
+ rescue Gem::Package::FormatError
+ end
+ end
+ else
+ local = Gem::Source::Local.new
+
+ if s = local.find_gem(gem_name, version)
+ set.add s, local
+ end
+ end
+ end
+
+ if set.empty?
+ dep = Gem::Dependency.new gem_name, version
+ dep.prerelease = true if prerelease
+
+ set = find_gems_with_sources(dep, true)
+ set.match_platform!
+ end
+
+ if set.empty?
+ raise Gem::SpecificGemNotFoundException.new(gem_name, version, @errors)
+ end
+
+ @available = set
+ end
+
+ ##
+ # Gathers all dependencies necessary for the installation from local and
+ # remote sources unless the ignore_dependencies was given.
+ #--
+ # TODO remove at RubyGems 3
+
+ def gather_dependencies # :nodoc:
+ specs = @available.all_specs
+
+ # these gems were listed by the user, always install them
+ keep_names = specs.map { |spec| spec.full_name }
+
+ if @dev_shallow
+ @toplevel_specs = keep_names
+ end
+
+ dependency_list = Gem::DependencyList.new @development
+ dependency_list.add(*specs)
+ to_do = specs.dup
+ 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|
+ not keep_names.include?(spec.full_name) and
+ Gem::Specification.include?(spec)
+ }
+
+ unless dependency_list.ok? or @ignore_dependencies or @force then
+ reason = dependency_list.why_not_ok?.map { |k,v|
+ "#{k} requires #{v.join(", ")}"
+ }.join("; ")
+ raise Gem::DependencyError, "Unable to resolve dependencies: #{reason}"
+ end
+
+ @gems_to_install = dependency_list.dependency_order.reverse
+ end
+
+ def in_background what # :nodoc:
+ 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
+
+ ##
+ # Installs the gem +dep_or_name+ and all its dependencies. Returns an Array
+ # of installed gem specifications.
+ #
+ # If the +:prerelease+ option is set and there is a prerelease for
+ # +dep_or_name+ the prerelease version will be installed.
+ #
+ # Unless explicitly specified as a prerelease dependency, prerelease gems
+ # that +dep_or_name+ depend on will not be installed.
+ #
+ # If c-1.a depends on b-1 and a-1.a and there is a gem b-1.a available then
+ # c-1.a, b-1 and a-1.a will be installed. b-1.a will need to be installed
+ # separately.
+
+ def install dep_or_name, version = Gem::Requirement.default
+ request_set = resolve_dependencies dep_or_name, version
+
+ @installed_gems = []
+
+ options = {
+ :bin_dir => @bin_dir,
+ :build_args => @build_args,
+ :document => @document,
+ :env_shebang => @env_shebang,
+ :force => @force,
+ :format_executable => @format_executable,
+ :ignore_dependencies => @ignore_dependencies,
+ :prerelease => @prerelease,
+ :security_policy => @security_policy,
+ :user_install => @user_install,
+ :wrappers => @wrappers,
+ :build_root => @build_root,
+ :install_as_default => @install_as_default
+ }
+ options[:install_dir] = @install_dir if @only_install_dir
+
+ request_set.install options do |_, installer|
+ @installed_gems << installer.spec if installer
+ end
+
+ @installed_gems.sort!
+
+ # 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
+ Gem.done_installing_hooks.each do |hook|
+ hook.call self, @installed_gems
+ end
+ end unless Gem.done_installing_hooks.empty?
+
+ @installed_gems
+ end
+
+ def install_development_deps # :nodoc:
+ if @development and @dev_shallow then
+ :shallow
+ elsif @development then
+ :all
+ else
+ :none
+ end
+ end
+
+ def resolve_dependencies dep_or_name, version # :nodoc:
+ request_set = Gem::RequestSet.new
+ request_set.development = @development
+ request_set.development_shallow = @dev_shallow
+ request_set.soft_missing = @force
+ request_set.prerelease = @prerelease
+ request_set.remote = false unless consider_remote?
+
+ installer_set = Gem::Resolver::InstallerSet.new @domain
+ installer_set.ignore_installed = @only_install_dir
+
+ if consider_local?
+ if dep_or_name =~ /\.gem$/ and File.file? dep_or_name then
+ src = Gem::Source::SpecificFile.new dep_or_name
+ installer_set.add_local dep_or_name, src.spec, src
+ version = src.spec.version if version == Gem::Requirement.default
+ elsif dep_or_name =~ /\.gem$/ then
+ Dir[dep_or_name].each do |name|
+ begin
+ src = Gem::Source::SpecificFile.new name
+ installer_set.add_local dep_or_name, src.spec, src
+ rescue Gem::Package::FormatError
+ end
+ end
+ # else This is a dependency. InstallerSet handles this case
+ end
+ end
+
+ dependency =
+ if spec = installer_set.local?(dep_or_name) then
+ Gem::Dependency.new spec.name, version
+ elsif String === dep_or_name then
+ Gem::Dependency.new dep_or_name, version
+ else
+ dep_or_name
+ end
+
+ dependency.prerelease = @prerelease
+
+ request_set.import [dependency]
+
+ installer_set.add_always_install dependency
+
+ request_set.always_install = installer_set.always_install
+
+ if @ignore_dependencies then
+ installer_set.ignore_dependencies = true
+ request_set.ignore_dependencies = true
+ request_set.soft_missing = true
+ end
+
+ request_set.resolve installer_set
+
+ @errors.concat request_set.errors
+
+ request_set
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/dependency_list.rb b/jni/ruby/lib/rubygems/dependency_list.rb
new file mode 100644
index 0000000..ad7a82a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/dependency_list.rb
@@ -0,0 +1,240 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'tsort'
+require 'rubygems/deprecate'
+
+##
+# Gem::DependencyList is used for installing and uninstalling gems in the
+# 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
+ attr_reader :specs
+
+ include Enumerable
+ include TSort
+
+ ##
+ # Allows enabling/disabling use of development dependencies
+
+ attr_accessor :development
+
+ ##
+ # Creates a DependencyList from the current specs.
+
+ def self.from_specs
+ list = new
+ list.add(*Gem::Specification.to_a)
+ list
+ end
+
+ ##
+ # Creates a new DependencyList. If +development+ is true, development
+ # dependencies will be included.
+
+ def initialize development = false
+ @specs = []
+
+ @development = development
+ end
+
+ ##
+ # Adds +gemspecs+ to the dependency list.
+
+ def add(*gemspecs)
+ @specs.concat gemspecs
+ end
+
+ def clear
+ @specs.clear
+ end
+
+ ##
+ # Return a list of the gem specifications in the dependency list, sorted in
+ # order so that no gemspec in the list depends on a gemspec earlier in the
+ # list.
+ #
+ # This is useful when removing gems from a set of installed gems. By
+ # removing them in the returned order, you don't get into as many dependency
+ # issues.
+ #
+ # If there are circular dependencies (yuck!), then gems will be returned in
+ # order until only the circular dependents and anything they reference are
+ # left. Then arbitrary gemspecs will be returned until the circular
+ # dependency is broken, after which gems will be returned in dependency
+ # order again.
+
+ def dependency_order
+ sorted = strongly_connected_components.flatten
+
+ result = []
+ seen = {}
+
+ sorted.each do |spec|
+ if index = seen[spec.name] then
+ if result[index].version < spec.version then
+ result[index] = spec
+ end
+ else
+ seen[spec.name] = result.length
+ result << spec
+ end
+ end
+
+ result.reverse
+ end
+
+ ##
+ # Iterator over dependency_order
+
+ def each(&block)
+ dependency_order.each(&block)
+ end
+
+ def find_name(full_name)
+ @specs.find { |spec| spec.full_name == full_name }
+ end
+
+ def inspect # :nodoc:
+ "#<%s:0x%x %p>" % [self.class, object_id, map { |s| s.full_name }]
+ end
+
+ ##
+ # Are all the dependencies in the list satisfied?
+
+ def ok?
+ why_not_ok?(:quick).empty?
+ end
+
+ def why_not_ok? quick = false
+ unsatisfied = Hash.new { |h,k| h[k] = [] }
+ each do |spec|
+ spec.runtime_dependencies.each do |dep|
+ inst = Gem::Specification.any? { |installed_spec|
+ dep.name == installed_spec.name and
+ dep.requirement.satisfied_by? installed_spec.version
+ }
+
+ unless inst or @specs.find { |s| s.satisfies_requirement? dep } then
+ unsatisfied[spec.name] << dep
+ return unsatisfied if quick
+ end
+ end
+ end
+
+ unsatisfied
+ end
+
+ ##
+ # Is is ok to remove a gemspec from the dependency list?
+ #
+ # If removing the gemspec creates breaks a currently ok dependency, then it
+ # is NOT ok to remove the gemspec.
+
+ def ok_to_remove?(full_name, check_dev=true)
+ gem_to_remove = find_name full_name
+
+ siblings = @specs.find_all { |s|
+ s.name == gem_to_remove.name &&
+ s.full_name != gem_to_remove.full_name
+ }
+
+ deps = []
+
+ @specs.each do |spec|
+ check = check_dev ? spec.dependencies : spec.runtime_dependencies
+
+ check.each do |dep|
+ deps << dep if gem_to_remove.satisfies_requirement?(dep)
+ end
+ end
+
+ deps.all? { |dep|
+ siblings.any? { |s|
+ s.satisfies_requirement? dep
+ }
+ }
+ end
+
+ ##
+ # Remove everything in the DependencyList that matches but doesn't
+ # satisfy items in +dependencies+ (a hash of gem names to arrays of
+ # dependencies).
+
+ def remove_specs_unsatisfied_by dependencies
+ specs.reject! { |spec|
+ dep = dependencies[spec.name]
+ dep and not dep.requirement.satisfied_by? spec.version
+ }
+ end
+
+ ##
+ # Removes the gemspec matching +full_name+ from the dependency list
+
+ def remove_by_name(full_name)
+ @specs.delete_if { |spec| spec.full_name == full_name }
+ end
+
+ ##
+ # Return a hash of predecessors. <tt>result[spec]</tt> is an Array of
+ # gemspecs that have a dependency satisfied by the named gemspec.
+
+ def spec_predecessors
+ result = Hash.new { |h,k| h[k] = [] }
+
+ specs = @specs.sort.reverse
+
+ specs.each do |spec|
+ specs.each do |other|
+ next if spec == other
+
+ other.dependencies.each do |dep|
+ if spec.satisfies_requirement? dep then
+ result[spec] << other
+ end
+ end
+ end
+ end
+
+ result
+ end
+
+ def tsort_each_node(&block)
+ @specs.each(&block)
+ end
+
+ def tsort_each_child(node)
+ specs = @specs.sort.reverse
+
+ dependencies = node.runtime_dependencies
+ dependencies.push(*node.development_dependencies) if @development
+
+ dependencies.each do |dep|
+ specs.each do |spec|
+ if spec.satisfies_requirement? dep then
+ yield spec
+ break
+ end
+ end
+ end
+ end
+
+ private
+
+ ##
+ # Count the number of gemspecs in the list +specs+ that are not in
+ # +ignored+.
+
+ def active_count(specs, ignored)
+ specs.count { |spec| ignored[spec.full_name].nil? }
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/deprecate.rb b/jni/ruby/lib/rubygems/deprecate.rb
new file mode 100644
index 0000000..e19360d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/deprecate.rb
@@ -0,0 +1,70 @@
+##
+# Provides a single method +deprecate+ to be used to declare when
+# something is going away.
+#
+# class Legacy
+# def self.klass_method
+# # ...
+# end
+#
+# def instance_method
+# # ...
+# end
+#
+# extend Gem::Deprecate
+# deprecate :instance_method, "X.z", 2011, 4
+#
+# class << self
+# extend Gem::Deprecate
+# deprecate :klass_method, :none, 2011, 4
+# end
+# end
+
+module Gem::Deprecate
+
+ def self.skip # :nodoc:
+ @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|
+ 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
+
diff --git a/jni/ruby/lib/rubygems/doctor.rb b/jni/ruby/lib/rubygems/doctor.rb
new file mode 100644
index 0000000..3c71fd5
--- /dev/null
+++ b/jni/ruby/lib/rubygems/doctor.rb
@@ -0,0 +1,131 @@
+require 'rubygems'
+require 'rubygems/user_interaction'
+
+##
+# Cleans up after a partially-failed uninstall or for an invalid
+# Gem::Specification.
+#
+# If a specification was removed by hand this will remove any remaining files.
+#
+# If a corrupt specification was installed this will clean up warnings by
+# removing the bogus specification.
+
+class Gem::Doctor
+
+ include Gem::UserInteraction
+
+ ##
+ # Maps a gem subdirectory to the files that are expected to exist in the
+ # subdirectory.
+
+ REPOSITORY_EXTENSION_MAP = [ # :nodoc:
+ ['specifications', '.gemspec'],
+ ['build_info', '.info'],
+ ['cache', '.gem'],
+ ['doc', ''],
+ ['extensions', ''],
+ ['gems', ''],
+ ]
+
+ missing =
+ Gem::REPOSITORY_SUBDIRECTORIES.sort -
+ REPOSITORY_EXTENSION_MAP.map { |(k,_)| k }.sort
+
+ raise "Update REPOSITORY_EXTENSION_MAP, missing: #{missing.join ', '}" unless
+ missing.empty?
+
+ ##
+ # Creates a new Gem::Doctor that will clean up +gem_repository+. Only one
+ # gem repository may be cleaned at a time.
+ #
+ # If +dry_run+ is true no files or directories will be removed.
+
+ def initialize gem_repository, dry_run = false
+ @gem_repository = gem_repository
+ @dry_run = dry_run
+
+ @installed_specs = nil
+ end
+
+ ##
+ # Specs installed in this gem repository
+
+ def installed_specs # :nodoc:
+ @installed_specs ||= Gem::Specification.map { |s| s.full_name }
+ end
+
+ ##
+ # Are we doctoring a gem repository?
+
+ def gem_repository?
+ not installed_specs.empty?
+ end
+
+ ##
+ # Cleans up uninstalled files and invalid gem specifications
+
+ def doctor
+ @orig_home = Gem.dir
+ @orig_path = Gem.path
+
+ say "Checking #{@gem_repository}"
+
+ Gem.use_paths @gem_repository.to_s
+
+ unless gem_repository? then
+ say 'This directory does not appear to be a RubyGems repository, ' +
+ 'skipping'
+ say
+ return
+ end
+
+ doctor_children
+
+ say
+ ensure
+ Gem.use_paths @orig_home, *@orig_path
+ end
+
+ ##
+ # Cleans up children of this gem repository
+
+ def doctor_children # :nodoc:
+ REPOSITORY_EXTENSION_MAP.each do |sub_directory, extension|
+ doctor_child sub_directory, extension
+ end
+ end
+
+ ##
+ # Removes files in +sub_directory+ with +extension+
+
+ def doctor_child sub_directory, extension # :nodoc:
+ directory = File.join(@gem_repository, sub_directory)
+
+ Dir.entries(directory).sort.each do |ent|
+ next if ent == "." || ent == ".."
+
+ child = File.join(directory, ent)
+ next unless File.exist?(child)
+
+ basename = File.basename(child, extension)
+ next if installed_specs.include? basename
+ next if /^rubygems-\d/ =~ basename
+ next if 'specifications' == sub_directory and 'default' == basename
+
+ type = File.directory?(child) ? 'directory' : 'file'
+
+ action = if @dry_run then
+ 'Extra'
+ else
+ FileUtils.rm_r(child)
+ 'Removed'
+ end
+
+ say "#{action} #{type} #{sub_directory}/#{File.basename(child)}"
+ end
+ rescue Errno::ENOENT
+ # ignore
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/errors.rb b/jni/ruby/lib/rubygems/errors.rb
new file mode 100644
index 0000000..9defb9e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/errors.rb
@@ -0,0 +1,137 @@
+#--
+# This file contains all the various exceptions and other errors that are used
+# inside of RubyGems.
+#
+# DOC: Confirm _all_
+#++
+
+module Gem
+ ##
+ # 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.
+
+ class LoadError < ::LoadError
+ # Name of gem
+ attr_accessor :name
+
+ # Version requirement of gem
+ attr_accessor :requirement
+ end
+
+ # Raised when there are conflicting gem specs loaded
+
+ class ConflictError < LoadError
+
+ ##
+ # A Hash mapping conflicting specifications to the dependencies that
+ # caused the conflict
+
+ attr_reader :conflicts
+
+ ##
+ # The specification that had the conflict
+
+ attr_reader :target
+
+ def initialize target, conflicts
+ @target = target
+ @conflicts = conflicts
+ @name = target.name
+
+ reason = conflicts.map { |act, dependencies|
+ "#{act.full_name} conflicts with #{dependencies.join(", ")}"
+ }.join ", "
+
+ # TODO: improve message by saying who activated `con`
+
+ super("Unable to activate #{target.full_name}, because #{reason}")
+ end
+ end
+
+ class ErrorReason; end
+
+ # Generated when trying to lookup a gem to indicate that the gem
+ # 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
+
+ ##
+ # the name of the gem
+ attr_reader :name
+
+ ##
+ # 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 (%s), but was for platform%s %s" %
+ [@name,
+ @version,
+ @platforms.size == 1 ? '' : 's',
+ @platforms.join(' ,')]
+ end
+ end
+
+ ##
+ # An error that indicates we weren't able to fetch some
+ # data from a source
+
+ class SourceFetchProblem < ErrorReason
+
+ ##
+ # Creates a new SourceFetchProblem for the given +source+ and +error+.
+
+ def initialize(source, error)
+ @source = source
+ @error = error
+ end
+
+ ##
+ # The source that had the fetch problem.
+
+ attr_reader :source
+
+ ##
+ # The fetch error which is an Exception subclass.
+
+ attr_reader :error
+
+ ##
+ # An English description of the error.
+
+ def wordy
+ "Unable to download data from #{@source.uri} - #{@error.message}"
+ end
+
+ ##
+ # The "exception" alias allows you to call raise on a SourceFetchProblem.
+
+ alias exception error
+ end
+end
diff --git a/jni/ruby/lib/rubygems/exceptions.rb b/jni/ruby/lib/rubygems/exceptions.rb
new file mode 100644
index 0000000..2a9875c
--- /dev/null
+++ b/jni/ruby/lib/rubygems/exceptions.rb
@@ -0,0 +1,270 @@
+# 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
+# subclass of this one.
+class Gem::Exception < RuntimeError
+
+ ##
+ #--
+ # TODO: remove in RubyGems 3, nobody sets this
+
+ attr_accessor :source_exception # :nodoc:
+
+end
+
+class Gem::CommandLineError < Gem::Exception; end
+
+class Gem::DependencyError < Gem::Exception; end
+
+class Gem::DependencyRemovalException < Gem::Exception; end
+
+##
+# Raised by Gem::Resolver when a Gem::Dependency::Conflict reaches the
+# toplevel. Indicates which dependencies were incompatible through #conflict
+# and #conflicting_dependencies
+
+class Gem::DependencyResolutionError < Gem::DependencyError
+
+ attr_reader :conflict
+
+ def initialize conflict
+ @conflict = conflict
+ a, b = conflicting_dependencies
+
+ super "conflicting dependencies #{a} and #{b}\n#{@conflict.explanation}"
+ end
+
+ def conflicting_dependencies
+ @conflict.conflicting_dependencies
+ end
+
+end
+
+##
+# Raised when attempting to uninstall a gem that isn't in GEM_HOME.
+
+class Gem::GemNotInHomeException < Gem::Exception
+ attr_accessor :spec
+end
+
+class Gem::DocumentError < Gem::Exception; end
+
+##
+# Potentially raised when a specification is validated.
+class Gem::EndOfYAMLException < Gem::Exception; end
+
+##
+# Signals that a file permission error is preventing the user from
+# operating on the given directory.
+
+class Gem::FilePermissionError < Gem::Exception
+
+ attr_reader :directory
+
+ def initialize directory
+ @directory = directory
+
+ super "You don't have write permissions for the #{directory} directory."
+ end
+
+end
+
+##
+# Used to raise parsing and loading errors
+class Gem::FormatException < Gem::Exception
+ attr_accessor :file_path
+end
+
+class Gem::GemNotFoundException < Gem::Exception; end
+
+##
+# Raised by the DependencyInstaller when a specific gem cannot be found
+
+class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException
+
+ ##
+ # Creates a new SpecificGemNotFoundException for a gem with the given +name+
+ # and +version+. Any +errors+ encountered when attempting to find the gem
+ # are also stored.
+
+ def initialize(name, version, errors=nil)
+ super "Could not find a valid gem '#{name}' (#{version}) locally or in a repository"
+
+ @name = name
+ @version = version
+ @errors = errors
+ end
+
+ ##
+ # The name of the gem that could not be found.
+
+ attr_reader :name
+
+ ##
+ # The version of the gem that could not be found.
+
+ attr_reader :version
+
+ ##
+ # Errors encountered attempting to find the gem.
+
+ attr_reader :errors
+
+end
+
+##
+# Raised by Gem::Resolver when dependencies conflict and create the
+# inability to find a valid possible spec for a request.
+
+class Gem::ImpossibleDependenciesError < Gem::Exception
+
+ attr_reader :conflicts
+ attr_reader :request
+
+ def initialize request, conflicts
+ @request = request
+ @conflicts = conflicts
+
+ super build_message
+ end
+
+ def build_message # :nodoc:
+ requester = @request.requester
+ requester = requester ? requester.spec.full_name : 'The user'
+ dependency = @request.dependency
+
+ message = "#{requester} requires #{dependency} but it conflicted:\n"
+
+ @conflicts.each do |_, conflict|
+ message << conflict.explanation
+ end
+
+ message
+ end
+
+ def dependency
+ @request.dependency
+ end
+
+end
+
+class Gem::InstallError < Gem::Exception; end
+
+##
+# Potentially raised when a specification is validated.
+class Gem::InvalidSpecificationException < Gem::Exception; end
+
+class Gem::OperationNotSupportedError < Gem::Exception; end
+
+##
+# Signals that a remote operation cannot be conducted, probably due to not
+# being connected (or just not finding host).
+#--
+# TODO: create a method that tests connection to the preferred gems server.
+# All code dealing with remote operations will want this. Failure in that
+# method should raise this error.
+class Gem::RemoteError < Gem::Exception; end
+
+class Gem::RemoteInstallationCancelled < Gem::Exception; end
+
+class Gem::RemoteInstallationSkipped < Gem::Exception; end
+
+##
+# Represents an error communicating via HTTP.
+class Gem::RemoteSourceException < Gem::Exception; end
+
+##
+# Raised when a gem dependencies file specifies a ruby version that does not
+# match the current version.
+
+class Gem::RubyVersionMismatch < Gem::Exception; end
+
+##
+# Raised by Gem::Validator when something is not right in a gem.
+
+class Gem::VerificationError < Gem::Exception; end
+
+##
+# Raised to indicate that a system exit should occur with the specified
+# exit_code
+
+class Gem::SystemExitException < SystemExit
+
+ ##
+ # The exit code for the process
+
+ attr_accessor :exit_code
+
+ ##
+ # Creates a new SystemExitException with the given +exit_code+
+
+ def initialize(exit_code)
+ @exit_code = exit_code
+
+ super "Exiting RubyGems with exit_code #{exit_code}"
+ end
+
+end
+
+##
+# Raised by Resolver when a dependency requests a gem for which
+# there is no spec.
+
+class Gem::UnsatisfiableDependencyError < Gem::DependencyError
+
+ ##
+ # The unsatisfiable dependency. This is a
+ # Gem::Resolver::DependencyRequest, not a Gem::Dependency
+
+ attr_reader :dependency
+
+ ##
+ # Errors encountered which may have contributed to this exception
+
+ attr_accessor :errors
+
+ ##
+ # Creates a new UnsatisfiableDependencyError for the unsatisfiable
+ # Gem::Resolver::DependencyRequest +dep+
+
+ def initialize dep, platform_mismatch=nil
+ if platform_mismatch and !platform_mismatch.empty?
+ plats = platform_mismatch.map { |x| x.platform.to_s }.sort.uniq
+ super "Unable to resolve dependency: No match for '#{dep}' on this platform. Found: #{plats.join(', ')}"
+ else
+ if dep.explicit?
+ super "Unable to resolve dependency: user requested '#{dep}'"
+ else
+ super "Unable to resolve dependency: '#{dep.request_context}' requires '#{dep}'"
+ end
+ end
+
+ @dependency = dep
+ @errors = []
+ end
+
+ ##
+ # The name of the unresolved dependency
+
+ def name
+ @dependency.name
+ end
+
+ ##
+ # The Requirement of the unresolved dependency (not Version).
+
+ def version
+ @dependency.requirement
+ end
+
+end
+
+##
+# Backwards compatible typo'd exception class for early RubyGems 2.0.x
+
+Gem::UnsatisfiableDepedencyError = Gem::UnsatisfiableDependencyError # :nodoc:
+
diff --git a/jni/ruby/lib/rubygems/ext.rb b/jni/ruby/lib/rubygems/ext.rb
new file mode 100644
index 0000000..5af6bbf
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext.rb
@@ -0,0 +1,18 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+##
+# Classes for building C extensions live here.
+
+module Gem::Ext; end
+
+require 'rubygems/ext/build_error'
+require 'rubygems/ext/builder'
+require 'rubygems/ext/configure_builder'
+require 'rubygems/ext/ext_conf_builder'
+require 'rubygems/ext/rake_builder'
+require 'rubygems/ext/cmake_builder'
+
diff --git a/jni/ruby/lib/rubygems/ext/build_error.rb b/jni/ruby/lib/rubygems/ext/build_error.rb
new file mode 100644
index 0000000..bfe85ff
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext/build_error.rb
@@ -0,0 +1,6 @@
+##
+# Raised when there is an error while building extensions.
+
+class Gem::Ext::BuildError < Gem::InstallError
+end
+
diff --git a/jni/ruby/lib/rubygems/ext/builder.rb b/jni/ruby/lib/rubygems/ext/builder.rb
new file mode 100644
index 0000000..548f126
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext/builder.rb
@@ -0,0 +1,218 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/user_interaction'
+require 'thread'
+
+class Gem::Ext::Builder
+
+ include Gem::UserInteraction
+
+ ##
+ # The builder shells-out to run various commands after changing the
+ # directory. This means multiple installations cannot be allowed to build
+ # extensions in parallel as they may change each other's directories leading
+ # to broken extensions or failed installations.
+
+ CHDIR_MUTEX = Mutex.new # :nodoc:
+
+ attr_accessor :build_args # :nodoc:
+
+ def self.class_name
+ name =~ /Ext::(.*)Builder/
+ $1.downcase
+ end
+
+ def self.make(dest_path, results)
+ unless File.exist? 'Makefile' then
+ raise Gem::InstallError, 'Makefile not found'
+ end
+
+ # try to find make program from Ruby configure arguments first
+ RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
+ make_program = ENV['MAKE'] || ENV['make'] || $1
+ unless make_program then
+ make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
+ end
+
+ destdir = '"DESTDIR=%s"' % ENV['DESTDIR'] if RUBY_VERSION > '2.0'
+
+ ['clean', '', 'install'].each do |target|
+ # Pass DESTDIR via command line to override what's in MAKEFLAGS
+ cmd = [
+ make_program,
+ destdir,
+ target
+ ].join(' ').rstrip
+ begin
+ run(cmd, results, "make #{target}".rstrip)
+ rescue Gem::InstallError
+ raise unless target == 'clean' # ignore clean failure
+ end
+ end
+ end
+
+ def self.redirector
+ '2>&1'
+ end
+
+ def self.run(command, results, command_name = nil)
+ verbose = Gem.configuration.really_verbose
+
+ begin
+ # TODO use Process.spawn when ruby 1.8 support is dropped.
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
+ if verbose
+ puts(command)
+ system(command)
+ else
+ results << command
+ results << `#{command} #{redirector}`
+ end
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
+ unless $?.success? then
+ results << "Building has failed. See above output for more information on the failure." if verbose
+
+ exit_reason =
+ if $?.exited? then
+ ", exit code #{$?.exitstatus}"
+ elsif $?.signaled? then
+ ", uncaught signal #{$?.termsig}"
+ end
+
+ raise Gem::InstallError, "#{command_name || class_name} failed#{exit_reason}"
+ end
+ end
+
+ ##
+ # Creates a new extension builder for +spec+. If the +spec+ does not yet
+ # have build arguments, saved, set +build_args+ which is an ARGV-style
+ # array.
+
+ def initialize spec, build_args = spec.build_args
+ @spec = spec
+ @build_args = build_args
+ @gem_dir = spec.full_gem_path
+
+ @ran_rake = nil
+ end
+
+ ##
+ # Chooses the extension builder class for +extension+
+
+ def builder_for extension # :nodoc:
+ case extension
+ when /extconf/ then
+ Gem::Ext::ExtConfBuilder
+ when /configure/ then
+ Gem::Ext::ConfigureBuilder
+ when /rakefile/i, /mkrf_conf/i then
+ @ran_rake = true
+ Gem::Ext::RakeBuilder
+ when /CMakeLists.txt/ then
+ Gem::Ext::CmakeBuilder
+ else
+ extension_dir = File.join @gem_dir, File.dirname(extension)
+
+ message = "No builder for extension '#{extension}'"
+ build_error extension_dir, message
+ end
+ end
+
+ ##
+ # Logs the build +output+ in +build_dir+, then raises Gem::Ext::BuildError.
+
+ def build_error build_dir, output, backtrace = nil # :nodoc:
+ gem_make_out = write_gem_make_out output
+
+ message = <<-EOF
+ERROR: Failed to build gem native extension.
+
+ #{output}
+
+Gem files will remain installed in #{@gem_dir} for inspection.
+Results logged to #{gem_make_out}
+EOF
+
+ raise Gem::Ext::BuildError, message, backtrace
+ end
+
+ def build_extension extension, dest_path # :nodoc:
+ results = []
+
+ extension ||= '' # I wish I knew why this line existed
+ extension_dir =
+ File.expand_path File.join @gem_dir, File.dirname(extension)
+ lib_dir = File.join @spec.full_gem_path, @spec.raw_require_paths.first
+
+ builder = builder_for extension
+
+ begin
+ FileUtils.mkdir_p dest_path
+
+ CHDIR_MUTEX.synchronize do
+ Dir.chdir extension_dir do
+ results = builder.build(extension, @gem_dir, dest_path,
+ results, @build_args, lib_dir)
+
+ verbose { results.join("\n") }
+ end
+ end
+
+ write_gem_make_out results.join "\n"
+ rescue => e
+ results << e.message
+ build_error extension_dir, results.join("\n"), $@
+ end
+ end
+
+ ##
+ # Builds extensions. Valid types of extensions are extconf.rb files,
+ # configure scripts and rakefiles or mkrf_conf files.
+
+ def build_extensions
+ return if @spec.extensions.empty?
+
+ 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 = @spec.extension_dir
+
+ FileUtils.rm_f @spec.gem_build_complete_path
+
+ @ran_rake = false # only run rake once
+
+ @spec.extensions.each do |extension|
+ break if @ran_rake
+
+ build_extension extension, dest_path
+ end
+
+ FileUtils.touch @spec.gem_build_complete_path
+ end
+
+ ##
+ # Writes +output+ to gem_make.out in the extension install directory.
+
+ def write_gem_make_out output # :nodoc:
+ destination = File.join @spec.extension_dir, 'gem_make.out'
+
+ FileUtils.mkdir_p @spec.extension_dir
+
+ open destination, 'wb' do |io| io.puts output end
+
+ destination
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/ext/cmake_builder.rb b/jni/ruby/lib/rubygems/ext/cmake_builder.rb
new file mode 100644
index 0000000..24531bc
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext/cmake_builder.rb
@@ -0,0 +1,16 @@
+require 'rubygems/command'
+
+class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ unless File.exist?('Makefile') then
+ cmd = "cmake . -DCMAKE_INSTALL_PREFIX=#{dest_path}"
+ cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
+
+ run cmd, results
+ end
+
+ make dest_path, results
+
+ results
+ end
+end
diff --git a/jni/ruby/lib/rubygems/ext/configure_builder.rb b/jni/ruby/lib/rubygems/ext/configure_builder.rb
new file mode 100644
index 0000000..f66e393
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext/configure_builder.rb
@@ -0,0 +1,23 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
+
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ unless File.exist?('Makefile') then
+ cmd = "sh ./configure --prefix=#{dest_path}"
+ cmd << " #{args.join ' '}" unless args.empty?
+
+ run cmd, results
+ end
+
+ make dest_path, results
+
+ results
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/ext/ext_conf_builder.rb b/jni/ruby/lib/rubygems/ext/ext_conf_builder.rb
new file mode 100644
index 0000000..d11d1ac
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext/ext_conf_builder.rb
@@ -0,0 +1,78 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'fileutils'
+require 'tempfile'
+
+class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
+ FileEntry = FileUtils::Entry_ # :nodoc:
+
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ # relative path required as some versions of mktmpdir return an absolute
+ # path which breaks make if it includes a space in the name
+ tmp_dest = get_relative_path(Dir.mktmpdir(".gem.", "."))
+
+ t = nil
+ Tempfile.open %w"siteconf .rb", "." do |siteconf|
+ t = siteconf
+ siteconf.puts "require 'rbconfig'"
+ siteconf.puts "dest_path = #{tmp_dest.dump}"
+ %w[sitearchdir sitelibdir].each do |dir|
+ siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path"
+ siteconf.puts "RbConfig::CONFIG['#{dir}'] = dest_path"
+ end
+
+ siteconf.flush
+
+ destdir = ENV["DESTDIR"]
+
+ begin
+ cmd = [Gem.ruby, "-r", get_relative_path(siteconf.path), File.basename(extension), *args].join ' '
+
+ begin
+ run cmd, results
+ ensure
+ FileUtils.mv 'mkmf.log', dest_path if File.exist? 'mkmf.log'
+ siteconf.unlink
+ end
+
+ ENV["DESTDIR"] = nil
+
+ make dest_path, results
+
+ if tmp_dest
+ # TODO remove in RubyGems 3
+ if Gem.install_extension_in_lib and lib_dir then
+ FileUtils.mkdir_p lib_dir
+ entries = Dir.entries(tmp_dest) - %w[. ..]
+ entries = entries.map { |entry| File.join tmp_dest, entry }
+ FileUtils.cp_r entries, lib_dir, :remove_destination => true
+ end
+
+ FileEntry.new(tmp_dest).traverse do |ent|
+ destent = ent.class.new(dest_path, ent.rel)
+ destent.exist? or FileUtils.mv(ent.path, destent.path)
+ end
+ end
+ ensure
+ ENV["DESTDIR"] = destdir
+ end
+ end
+ t.unlink if t and t.path
+
+ results
+ ensure
+ FileUtils.rm_rf tmp_dest if tmp_dest
+ end
+
+ private
+ def self.get_relative_path(path)
+ path[0..Dir.pwd.length-1] = '.' if path.start_with?(Dir.pwd)
+ path
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/ext/rake_builder.rb b/jni/ruby/lib/rubygems/ext/rake_builder.rb
new file mode 100644
index 0000000..d5ebf59
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ext/rake_builder.rb
@@ -0,0 +1,36 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+class Gem::Ext::RakeBuilder < Gem::Ext::Builder
+
+ def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ if File.basename(extension) =~ /mkrf_conf/i then
+ cmd = "#{Gem.ruby} #{File.basename extension}"
+ cmd << " #{args.join " "}" unless args.empty?
+ run cmd, results
+ end
+
+ # Deal with possible spaces in the path, e.g. C:/Program Files
+ dest_path = '"' + dest_path.to_s + '"' if dest_path.to_s.include?(' ')
+
+ rake = ENV['rake']
+
+ rake ||= begin
+ "#{Gem.ruby} -rubygems #{Gem.bin_path('rake', 'rake')}"
+ rescue Gem::Exception
+ end
+
+ rake ||= Gem.default_exec_format % 'rake'
+
+ cmd = "#{rake} RUBYARCHDIR=#{dest_path} RUBYLIBDIR=#{dest_path}" # ENV is frozen
+
+ run cmd, results
+
+ results
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/gem_runner.rb b/jni/ruby/lib/rubygems/gem_runner.rb
new file mode 100644
index 0000000..7a3fd6b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/gem_runner.rb
@@ -0,0 +1,81 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems'
+require 'rubygems/command_manager'
+require 'rubygems/config_file'
+
+##
+# Load additional plugins from $LOAD_PATH
+
+Gem.load_env_plugins rescue nil
+
+##
+# Run an instance of the gem program.
+#
+# Gem::GemRunner is only intended for internal use by RubyGems itself. It
+# does not form any public API and may change at any time for any reason.
+#
+# If you would like to duplicate functionality of `gem` commands, use the
+# classes they call directly.
+
+class Gem::GemRunner
+
+ def initialize(options={})
+ # TODO: nuke these options
+ @command_manager_class = options[:command_manager] || Gem::CommandManager
+ @config_file_class = options[:config_file] || Gem::ConfigFile
+ end
+
+ ##
+ # Run the gem command with the following arguments.
+
+ def run args
+ build_args = extract_build_args args
+
+ do_configuration args
+
+ cmd = @command_manager_class.instance
+
+ cmd.command_names.each do |command_name|
+ config_args = Gem.configuration[command_name]
+ config_args = case config_args
+ when String
+ config_args.split ' '
+ else
+ Array(config_args)
+ end
+ Gem::Command.add_specific_extra_args command_name, config_args
+ end
+
+ cmd.run Gem.configuration.args, build_args
+ end
+
+ ##
+ # Separates the build arguments (those following <code>--</code>) from the
+ # other arguments in the list.
+
+ def extract_build_args args # :nodoc:
+ return [] unless offset = args.index('--')
+
+ build_args = args.slice!(offset...args.length)
+
+ build_args.shift
+
+ build_args
+ end
+
+ private
+
+ def do_configuration(args)
+ Gem.configuration = @config_file_class.new(args)
+ Gem.use_paths Gem.configuration[:gemhome], Gem.configuration[:gempath]
+ Gem::Command.extra_args = Gem.configuration[:gem]
+ end
+
+end
+
+Gem.load_plugins
diff --git a/jni/ruby/lib/rubygems/gemcutter_utilities.rb b/jni/ruby/lib/rubygems/gemcutter_utilities.rb
new file mode 100644
index 0000000..4ecf737
--- /dev/null
+++ b/jni/ruby/lib/rubygems/gemcutter_utilities.rb
@@ -0,0 +1,154 @@
+require 'rubygems/remote_fetcher'
+
+##
+# Utility methods for using the RubyGems API.
+
+module Gem::GemcutterUtilities
+
+ # TODO: move to Gem::Command
+ OptionParser.accept Symbol do |value|
+ value.to_sym
+ end
+
+ attr_writer :host
+
+ ##
+ # Add the --key option
+
+ def add_key_option
+ add_option('-k', '--key KEYNAME', Symbol,
+ 'Use the given API key',
+ 'from ~/.gem/credentials') do |value,options|
+ options[:key] = value
+ end
+ end
+
+ ##
+ # The API key from the command options or from the user's configuration.
+
+ def api_key
+ if options[:key] then
+ verify_api_key options[:key]
+ elsif Gem.configuration.api_keys.key?(host)
+ Gem.configuration.api_keys[host]
+ else
+ Gem.configuration.rubygems_api_key
+ end
+ end
+
+ ##
+ # The host to connect to either from the RUBYGEMS_HOST environment variable
+ # or from the user's configuration
+
+ def host
+ configured_host = Gem.host unless
+ Gem.configuration.disable_default_gem_server
+
+ @host ||=
+ begin
+ env_rubygems_host = ENV['RUBYGEMS_HOST']
+ env_rubygems_host = nil if
+ env_rubygems_host and env_rubygems_host.empty?
+
+ env_rubygems_host|| configured_host
+ end
+ end
+
+ ##
+ # Creates an RubyGems API to +host+ and +path+ with the given HTTP +method+.
+ #
+ # If +allowed_push_host+ metadata is present, then it will only allow that host.
+
+ def rubygems_api_request(method, path, host = nil, allowed_push_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
+
+ if allowed_push_host and self.host != allowed_push_host
+ alert_error "#{self.host.inspect} is not allowed by the gemspec, which only allows #{allowed_push_host.inspect}"
+ terminate_interaction 1
+ end
+
+ uri = URI.parse "#{self.host}/#{path}"
+
+ request_method = Net::HTTP.const_get method.to_s.capitalize
+
+ Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
+ end
+
+ ##
+ # Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API
+ # key.
+
+ def sign_in sign_in_host = nil
+ sign_in_host ||= self.host
+ return if api_key
+
+ pretty_host = if Gem::DEFAULT_HOST == sign_in_host then
+ 'RubyGems.org'
+ else
+ sign_in_host
+ end
+
+ say "Enter your #{pretty_host} credentials."
+ say "Don't have an account yet? " +
+ "Create one at #{sign_in_host}/sign_up"
+
+ email = ask " Email: "
+ password = ask_for_password "Password: "
+ say "\n"
+
+ response = rubygems_api_request(:get, "api/v1/api_key",
+ sign_in_host) do |request|
+ request.basic_auth email, password
+ end
+
+ with_response response do |resp|
+ say "Signed in."
+ Gem.configuration.rubygems_api_key = resp.body
+ end
+ end
+
+ ##
+ # Retrieves the pre-configured API key +key+ or terminates interaction with
+ # an error.
+
+ def verify_api_key(key)
+ if Gem.configuration.api_keys.key? key then
+ Gem.configuration.api_keys[key]
+ else
+ alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)."
+ terminate_interaction 1 # TODO: question this
+ end
+ end
+
+ ##
+ # If +response+ is an HTTP Success (2XX) response, yields the response if a
+ # block was given or shows the response body to the user.
+ #
+ # If the response was not successful, shows an error to the user including
+ # the +error_prefix+ and the response body.
+
+ def with_response response, error_prefix = nil
+ case response
+ when Net::HTTPSuccess then
+ if block_given? then
+ yield response
+ else
+ say response.body
+ end
+ else
+ message = response.body
+ message = "#{error_prefix}: #{message}" if error_prefix
+
+ say message
+ terminate_interaction 1 # TODO: question this
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/indexer.rb b/jni/ruby/lib/rubygems/indexer.rb
new file mode 100644
index 0000000..f94fb1b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/indexer.rb
@@ -0,0 +1,498 @@
+require 'rubygems'
+require 'rubygems/package'
+require 'time'
+
+begin
+ gem 'builder'
+ require 'builder/xchar'
+rescue LoadError
+end
+
+##
+# Top level class for building the gem repository index.
+
+class Gem::Indexer
+
+ include Gem::UserInteraction
+
+ ##
+ # Build indexes for RubyGems 1.2.0 and newer when true
+
+ attr_accessor :build_modern
+
+ ##
+ # Index install location
+
+ attr_reader :dest_directory
+
+ ##
+ # Specs index install location
+
+ attr_reader :dest_specs_index
+
+ ##
+ # Latest specs index install location
+
+ attr_reader :dest_latest_specs_index
+
+ ##
+ # Prerelease specs index install location
+
+ attr_reader :dest_prerelease_specs_index
+
+ ##
+ # Index build directory
+
+ attr_reader :directory
+
+ ##
+ # Create an indexer that will index the gems in +directory+.
+
+ def initialize(directory, options = {})
+ require 'fileutils'
+ require 'tmpdir'
+ require 'zlib'
+
+ unless defined?(Builder::XChar) then
+ raise "Gem::Indexer requires that the XML Builder library be installed:" +
+ "\n\tgem install builder"
+ end
+
+ options = { :build_modern => true }.merge options
+
+ @build_modern = options[:build_modern]
+
+ @dest_directory = directory
+ @directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}")
+
+ marshal_name = "Marshal.#{Gem.marshal_version}"
+
+ @master_index = File.join @directory, 'yaml'
+ @marshal_index = File.join @directory, marshal_name
+
+ @quick_dir = File.join @directory, 'quick'
+ @quick_marshal_dir = File.join @quick_dir, marshal_name
+ @quick_marshal_dir_base = File.join "quick", marshal_name # FIX: UGH
+
+ @quick_index = File.join @quick_dir, 'index'
+ @latest_index = File.join @quick_dir, 'latest_index'
+
+ @specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
+ @latest_specs_index =
+ File.join(@directory, "latest_specs.#{Gem.marshal_version}")
+ @prerelease_specs_index =
+ File.join(@directory, "prerelease_specs.#{Gem.marshal_version}")
+ @dest_specs_index =
+ File.join(@dest_directory, "specs.#{Gem.marshal_version}")
+ @dest_latest_specs_index =
+ File.join(@dest_directory, "latest_specs.#{Gem.marshal_version}")
+ @dest_prerelease_specs_index =
+ File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}")
+
+ @files = []
+ end
+
+ ##
+ # Abbreviate the spec for downloading. Abbreviated specs are only used for
+ # searching, downloading and related activities and do not need deployment
+ # specific information (e.g. list of files). So we abbreviate the spec,
+ # making it much smaller for quicker downloads.
+ #--
+ # TODO move to Gem::Specification
+
+ def abbreviate(spec)
+ spec.files = []
+ spec.test_files = []
+ spec.rdoc_options = []
+ spec.extra_rdoc_files = []
+ spec.cert_chain = []
+ spec
+ end
+
+ ##
+ # Build various indicies
+
+ def build_indicies
+ Gem::Specification.dirs = []
+ Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list))
+
+ build_marshal_gemspecs
+ build_modern_indicies if @build_modern
+
+ compress_indicies
+ end
+
+ ##
+ # Builds Marshal quick index gemspecs.
+
+ def build_marshal_gemspecs
+ count = Gem::Specification.count { |s| not s.default_gem? }
+ progress = ui.progress_reporter count,
+ "Generating Marshal quick index gemspecs for #{count} gems",
+ "Complete"
+
+ files = []
+
+ Gem.time 'Generated Marshal quick index gemspecs' do
+ Gem::Specification.each do |spec|
+ next if spec.default_gem?
+ spec_file_name = "#{spec.original_name}.gemspec.rz"
+ marshal_name = File.join @quick_marshal_dir, spec_file_name
+
+ marshal_zipped = Gem.deflate Marshal.dump(spec)
+ open marshal_name, 'wb' do |io| io.write marshal_zipped end
+
+ files << marshal_name
+
+ progress.updated spec.original_name
+ end
+
+ progress.done
+ end
+
+ @files << @quick_marshal_dir
+
+ files
+ end
+
+ ##
+ # Build a single index for RubyGems 1.2 and newer
+
+ def build_modern_index(index, file, name)
+ say "Generating #{name} index"
+
+ Gem.time "Generated #{name} index" do
+ open(file, 'wb') do |io|
+ specs = index.map do |*spec|
+ # We have to splat here because latest_specs is an array, while the
+ # others are hashes.
+ spec = spec.flatten.last
+ platform = spec.original_platform
+
+ # win32-api-1.0.4-x86-mswin32-60
+ unless String === platform then
+ alert_warning "Skipping invalid platform in gem: #{spec.full_name}"
+ next
+ end
+
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
+ [spec.name, spec.version, platform]
+ end
+
+ specs = compact_specs(specs)
+ Marshal.dump(specs, io)
+ end
+ end
+ end
+
+ ##
+ # Builds indicies for RubyGems 1.2 and newer. Handles full, latest, prerelease
+
+ def build_modern_indicies
+ specs = Gem::Specification.reject { |s| s.default_gem? }
+
+ prerelease, released = specs.partition { |s|
+ s.version.prerelease?
+ }
+ latest_specs =
+ Gem::Specification.latest_specs.reject { |s| s.default_gem? }
+
+ build_modern_index(released.sort, @specs_index, 'specs')
+ build_modern_index(latest_specs.sort, @latest_specs_index, 'latest specs')
+ build_modern_index(prerelease.sort, @prerelease_specs_index,
+ 'prerelease specs')
+
+ @files += [@specs_index,
+ "#{@specs_index}.gz",
+ @latest_specs_index,
+ "#{@latest_specs_index}.gz",
+ @prerelease_specs_index,
+ "#{@prerelease_specs_index}.gz"]
+ end
+
+ def map_gems_to_specs gems
+ gems.map { |gemfile|
+ if File.size(gemfile) == 0 then
+ alert_warning "Skipping zero-length gem: #{gemfile}"
+ next
+ end
+
+ begin
+ spec = Gem::Package.new(gemfile).spec
+ spec.loaded_from = gemfile
+
+ # HACK: fuck this shit - borks all tests that use pl1
+ # if File.basename(gemfile, ".gem") != spec.original_name then
+ # exp = spec.full_name
+ # exp << " (#{spec.original_name})" if
+ # spec.original_name != spec.full_name
+ # msg = "Skipping misnamed gem: #{gemfile} should be named #{exp}"
+ # alert_warning msg
+ # next
+ # end
+
+ abbreviate spec
+ sanitize spec
+
+ spec
+ rescue SignalException
+ alert_error "Received signal, exiting"
+ raise
+ rescue Exception => e
+ msg = ["Unable to process #{gemfile}",
+ "#{e.message} (#{e.class})",
+ "\t#{e.backtrace.join "\n\t"}"].join("\n")
+ alert_error msg
+ end
+ }.compact
+ end
+
+ ##
+ # Compresses indicies on disk
+ #--
+ # All future files should be compressed using gzip, not deflate
+
+ def compress_indicies
+ say "Compressing indicies"
+
+ Gem.time 'Compressed indicies' do
+ if @build_modern then
+ gzip @specs_index
+ gzip @latest_specs_index
+ gzip @prerelease_specs_index
+ end
+ end
+ end
+
+ ##
+ # Compacts Marshal output for the specs index data source by using identical
+ # objects as much as possible.
+
+ def compact_specs(specs)
+ names = {}
+ versions = {}
+ platforms = {}
+
+ specs.map do |(name, version, platform)|
+ names[name] = name unless names.include? name
+ versions[version] = version unless versions.include? version
+ platforms[platform] = platform unless platforms.include? platform
+
+ [names[name], versions[version], platforms[platform]]
+ end
+ end
+
+ ##
+ # Compress +filename+ with +extension+.
+
+ def compress(filename, extension)
+ data = Gem.read_binary filename
+
+ zipped = Gem.deflate data
+
+ open "#{filename}.#{extension}", 'wb' do |io|
+ io.write zipped
+ end
+ end
+
+ ##
+ # List of gem file names to index.
+
+ def gem_file_list
+ Dir[File.join(@dest_directory, "gems", '*.gem')]
+ end
+
+ ##
+ # Builds and installs indicies.
+
+ def generate_index
+ make_temp_directories
+ build_indicies
+ install_indicies
+ rescue SignalException
+ ensure
+ FileUtils.rm_rf @directory
+ end
+
+ ##
+ # Zlib::GzipWriter wrapper that gzips +filename+ on disk.
+
+ def gzip(filename)
+ Zlib::GzipWriter.open "#{filename}.gz" do |io|
+ io.write Gem.read_binary(filename)
+ end
+ end
+
+ ##
+ # Install generated indicies into the destination directory.
+
+ def install_indicies
+ verbose = Gem.configuration.really_verbose
+
+ say "Moving index into production dir #{@dest_directory}" if verbose
+
+ files = @files
+ files.delete @quick_marshal_dir if files.include? @quick_dir
+
+ if files.include? @quick_marshal_dir and not files.include? @quick_dir then
+ files.delete @quick_marshal_dir
+
+ dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
+
+ FileUtils.mkdir_p File.dirname(dst_name), :verbose => verbose
+ FileUtils.rm_rf dst_name, :verbose => verbose
+ FileUtils.mv(@quick_marshal_dir, dst_name,
+ :verbose => verbose, :force => true)
+ end
+
+ files = files.map do |path|
+ path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK?
+ end
+
+ files.each do |file|
+ src_name = File.join @directory, file
+ dst_name = File.join @dest_directory, file
+
+ FileUtils.rm_rf dst_name, :verbose => verbose
+ FileUtils.mv(src_name, @dest_directory,
+ :verbose => verbose, :force => true)
+ end
+ end
+
+ ##
+ # Make directories for index generation
+
+ def make_temp_directories
+ FileUtils.rm_rf @directory
+ FileUtils.mkdir_p @directory, :mode => 0700
+ FileUtils.mkdir_p @quick_marshal_dir
+ end
+
+ ##
+ # Ensure +path+ and path with +extension+ are identical.
+
+ def paranoid(path, extension)
+ data = Gem.read_binary path
+ compressed_data = Gem.read_binary "#{path}.#{extension}"
+
+ unless data == Gem.inflate(compressed_data) then
+ raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
+ end
+ end
+
+ ##
+ # Sanitize the descriptive fields in the spec. Sometimes non-ASCII
+ # characters will garble the site index. Non-ASCII characters will
+ # be replaced by their XML entity equivalent.
+
+ def sanitize(spec)
+ spec.summary = sanitize_string(spec.summary)
+ spec.description = sanitize_string(spec.description)
+ spec.post_install_message = sanitize_string(spec.post_install_message)
+ spec.authors = spec.authors.collect { |a| sanitize_string(a) }
+
+ spec
+ end
+
+ ##
+ # Sanitize a single string.
+
+ def sanitize_string(string)
+ return string unless string
+
+ # HACK the #to_s is in here because RSpec has an Array of Arrays of
+ # Strings for authors. Need a way to disallow bad values on gemspec
+ # generation. (Probably won't happen.)
+ string = string.to_s
+
+ begin
+ Builder::XChar.encode string
+ rescue NameError, NoMethodError
+ string.to_xs
+ end
+ end
+
+ ##
+ # Perform an in-place update of the repository from newly added gems.
+
+ def update_index
+ make_temp_directories
+
+ specs_mtime = File.stat(@dest_specs_index).mtime
+ newest_mtime = Time.at 0
+
+ updated_gems = gem_file_list.select do |gem|
+ gem_mtime = File.stat(gem).mtime
+ newest_mtime = gem_mtime if gem_mtime > newest_mtime
+ gem_mtime >= specs_mtime
+ end
+
+ if updated_gems.empty? then
+ say 'No new gems'
+ terminate_interaction 0
+ end
+
+ specs = map_gems_to_specs updated_gems
+ prerelease, released = specs.partition { |s| s.version.prerelease? }
+
+ Gem::Specification.dirs = []
+ Gem::Specification.add_specs(*specs)
+
+ files = build_marshal_gemspecs
+
+ Gem.time 'Updated indexes' do
+ update_specs_index released, @dest_specs_index, @specs_index
+ update_specs_index released, @dest_latest_specs_index, @latest_specs_index
+ update_specs_index(prerelease,
+ @dest_prerelease_specs_index,
+ @prerelease_specs_index)
+ end
+
+ compress_indicies
+
+ verbose = Gem.configuration.really_verbose
+
+ say "Updating production dir #{@dest_directory}" if verbose
+
+ files << @specs_index
+ files << "#{@specs_index}.gz"
+ files << @latest_specs_index
+ files << "#{@latest_specs_index}.gz"
+ files << @prerelease_specs_index
+ files << "#{@prerelease_specs_index}.gz"
+
+ files = files.map do |path|
+ path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK?
+ end
+
+ files.each do |file|
+ src_name = File.join @directory, file
+ dst_name = File.join @dest_directory, file # REFACTOR: duped above
+
+ FileUtils.mv src_name, dst_name, :verbose => verbose,
+ :force => true
+
+ File.utime newest_mtime, newest_mtime, dst_name
+ end
+ end
+
+ ##
+ # Combines specs in +index+ and +source+ then writes out a new copy to
+ # +dest+. For a latest index, does not ensure the new file is minimal.
+
+ def update_specs_index(index, source, dest)
+ specs_index = Marshal.load Gem.read_binary(source)
+
+ index.each do |spec|
+ platform = spec.original_platform
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
+ specs_index << [spec.name, spec.version, platform]
+ end
+
+ specs_index = compact_specs specs_index.uniq.sort
+
+ open dest, 'wb' do |io|
+ Marshal.dump specs_index, io
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/install_default_message.rb b/jni/ruby/lib/rubygems/install_default_message.rb
new file mode 100644
index 0000000..458ba3d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/install_default_message.rb
@@ -0,0 +1,12 @@
+require 'rubygems'
+require 'rubygems/user_interaction'
+
+##
+# A post-install hook that displays "Successfully installed
+# some_gem-1.0 as a default gem"
+
+Gem.post_install do |installer|
+ ui = Gem::DefaultUserInteraction.ui
+ ui.say "Successfully installed #{installer.spec.full_name} as a default gem"
+end
+
diff --git a/jni/ruby/lib/rubygems/install_message.rb b/jni/ruby/lib/rubygems/install_message.rb
new file mode 100644
index 0000000..c1979c1
--- /dev/null
+++ b/jni/ruby/lib/rubygems/install_message.rb
@@ -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
+
diff --git a/jni/ruby/lib/rubygems/install_update_options.rb b/jni/ruby/lib/rubygems/install_update_options.rb
new file mode 100644
index 0000000..a503b7f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/install_update_options.rb
@@ -0,0 +1,186 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems'
+
+# forward-declare
+
+module Gem::Security # :nodoc:
+ class Policy # :nodoc:
+ end
+end
+
+##
+# Mixin methods for install and update options for Gem::Commands
+
+module Gem::InstallUpdateOptions
+
+ ##
+ # Add the install/update options to the option parser.
+
+ def add_install_update_options
+ # TODO: use @parser.accept
+ OptionParser.accept Gem::Security::Policy do |value|
+ require 'rubygems/security'
+
+ raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
+ defined?(Gem::Security::HighSecurity)
+
+ value = Gem::Security::Policies[value]
+ valid = Gem::Security::Policies.keys.sort
+ message = "#{value} (#{valid.join ', '} are valid)"
+ raise OptionParser::InvalidArgument, message if value.nil?
+ value
+ end
+
+ add_option(:"Install/Update", '-i', '--install-dir DIR',
+ 'Gem repository directory to get installed',
+ 'gems') do |value, options|
+ options[:install_dir] = File.expand_path(value)
+ end
+
+ add_option(:"Install/Update", '-n', '--bindir DIR',
+ 'Directory where binary files are',
+ 'located') do |value, options|
+ options[:bin_dir] = File.expand_path(value)
+ end
+
+ add_option(:"Install/Update", '--[no-]document [TYPES]', Array,
+ 'Generate documentation for installed gems',
+ 'List the documentation types you wish to',
+ 'generate. For example: rdoc,ri') do |value, options|
+ options[:document] = case value
+ when nil then %w[ri]
+ when false then []
+ else value
+ end
+ end
+
+ add_option(:"Install/Update", '--build-root DIR',
+ 'Temporary installation root. Useful for building',
+ 'packages. Do not use this when installing remote gems.') do |value, options|
+ options[:build_root] = File.expand_path(value)
+ end
+
+ add_option(:"Install/Update", '--vendor',
+ 'Install gem into the vendor directory.',
+ 'Only for use by gem repackagers.') do |value, options|
+ unless Gem.vendor_dir then
+ raise OptionParser::InvalidOption.new 'your platform is not supported'
+ end
+
+ options[:vendor] = true
+ options[:install_dir] = Gem.vendor_dir
+ end
+
+ add_option(:"Install/Update", '-N', '--no-document',
+ 'Disable documentation generation') do |value, options|
+ options[:document] = []
+ 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
+
+ add_option(:"Install/Update", '-E', '--[no-]env-shebang',
+ "Rewrite the shebang line on installed",
+ "scripts to use /usr/bin/env") do |value, options|
+ options[:env_shebang] = value
+ end
+
+ add_option(:"Install/Update", '-f', '--[no-]force',
+ 'Force gem to install, bypassing dependency',
+ 'checks') do |value, options|
+ options[:force] = value
+ end
+
+ add_option(:"Install/Update", '-w', '--[no-]wrappers',
+ 'Use bin wrappers for executables',
+ 'Not available on dosish platforms') do |value, options|
+ options[:wrappers] = value
+ end
+
+ add_option(:"Install/Update", '-P', '--trust-policy POLICY',
+ Gem::Security::Policy,
+ 'Specify gem trust policy') do |value, options|
+ options[:security_policy] = value
+ end
+
+ add_option(:"Install/Update", '--ignore-dependencies',
+ 'Do not install any required dependent gems') do |value, options|
+ options[:ignore_dependencies] = value
+ end
+
+ add_option(:"Install/Update", '--[no-]format-executable',
+ 'Make installed executable names match ruby.',
+ 'If ruby is ruby18, foo_exec will be',
+ 'foo_exec18') do |value, options|
+ options[:format_executable] = value
+ end
+
+ add_option(:"Install/Update", '--[no-]user-install',
+ 'Install in user\'s home directory instead',
+ 'of GEM_HOME.') do |value, options|
+ options[:user_install] = value
+ end
+
+ add_option(:"Install/Update", "--development",
+ "Install additional development",
+ "dependencies") do |value, options|
+ 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
+
+ add_option(:"Install/Update", "--conservative",
+ "Don't attempt to upgrade gems already",
+ "meeting version requirement") do |value, options|
+ 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
+
+ ##
+ # Default options for the gem install command.
+
+ def install_update_defaults_str
+ '--document=rdoc,ri --wrappers'
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/installer.rb b/jni/ruby/lib/rubygems/installer.rb
new file mode 100644
index 0000000..877cb21
--- /dev/null
+++ b/jni/ruby/lib/rubygems/installer.rb
@@ -0,0 +1,819 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/command'
+require 'rubygems/exceptions'
+require 'rubygems/package'
+require 'rubygems/ext'
+require 'rubygems/user_interaction'
+require 'fileutils'
+
+##
+# The installer installs the files contained in the .gem into the Gem.home.
+#
+# 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
+# gemspec in the specifications dir, storing the cached gem in the cache dir,
+# and installing either wrappers or symlinks for executables.
+#
+# The installer invokes pre and post install hooks. Hooks can be added either
+# through a rubygems_plugin.rb file in an installed gem or via a
+# rubygems/defaults/#{RUBY_ENGINE}.rb or rubygems/defaults/operating_system.rb
+# file. See Gem.pre_install and Gem.post_install for details.
+
+class Gem::Installer
+
+ ##
+ # Paths where env(1) might live. Some systems are broken and have it in
+ # /bin
+
+ ENV_PATHS = %w[/usr/bin/env /bin/env]
+
+ ##
+ # Deprecated in favor of Gem::Ext::BuildError
+
+ ExtensionBuildError = Gem::Ext::BuildError # :nodoc:
+
+ include Gem::UserInteraction
+
+ ##
+ # Filename of the gem being installed.
+
+ attr_reader :gem
+
+ ##
+ # The directory a gem's executables will be installed into
+
+ attr_reader :bin_dir
+
+ attr_reader :build_root # :nodoc:
+
+ ##
+ # The gem repository the gem will be installed into
+
+ attr_reader :gem_home
+
+ ##
+ # The options passed when the Gem::Installer was instantiated.
+
+ attr_reader :options
+
+ ##
+ # Sets the specification for .gem-less installs.
+
+ attr_writer :spec
+
+ @path_warning = false
+
+ @install_lock = Mutex.new
+
+ class << self
+
+ ##
+ # True if we've warned about PATH not including Gem.bindir
+
+ attr_accessor :path_warning
+
+ ##
+ # Certain aspects of the install process are not thread-safe. This lock is
+ # used to allow multiple threads to install Gems at the same time.
+
+ attr_reader :install_lock
+
+ ##
+ # Overrides the executable format.
+ #
+ # This is a sprintf format with a "%s" which will be replaced with the
+ # executable name. It is based off the ruby executable name's difference
+ # from "ruby".
+
+ attr_writer :exec_format
+
+ # Defaults to use Ruby's program prefix and suffix.
+ def exec_format
+ @exec_format ||= Gem.default_exec_format
+ end
+
+ end
+
+ ##
+ # Constructs an Installer instance that will install the gem located at
+ # +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.
+ # :force:: Overrides all version checks and security policy checks, except
+ # for a signed-gems-only policy.
+ # :format_executable:: Format the executable the same as the Ruby executable.
+ # If your Ruby is ruby18, foo_exec will be installed as
+ # 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
+ # :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.
+ # :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={})
+ require 'fileutils'
+
+ @gem = gem
+ @options = options
+ @package = Gem::Package.new @gem
+
+ process_options
+
+ @package.security_policy = @security_policy
+
+ if options[:user_install] and not options[:unpack] then
+ @gem_home = Gem.user_dir
+ @bin_dir = Gem.bindir gem_home unless options[:bin_dir]
+ check_that_user_bin_dir_is_in_path
+ end
+ end
+
+ ##
+ # 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 check_executable_overwrite filename # :nodoc:
+ return if @force
+
+ generated_bin = File.join @bin_dir, formatted_program_filename(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
+
+ ##
+ # Lazy accessor for the spec's gem directory.
+
+ def gem_dir
+ @gem_dir ||= File.join(gem_home, "gems", spec.full_name)
+ end
+
+ ##
+ # Lazy accessor for the installer's spec.
+
+ def spec
+ @spec ||= @package.spec
+ rescue Gem::Package::Error => e
+ raise Gem::InstallError, "invalid gem: #{e.message}"
+ end
+
+ ##
+ # Installs the gem and returns a loaded Gem::Specification for the installed
+ # gem.
+ #
+ # The gem will be installed with the following structure:
+ #
+ # @gem_home/
+ # cache/<gem-version>.gem #=> a cached copy of the installed gem
+ # gems/<gem-version>/... #=> extracted files
+ # specifications/<gem-version>.gemspec #=> the Gem::Specification
+
+ def install
+ pre_install_checks
+
+ FileUtils.rm_f File.join gem_home, 'specifications', @spec.spec_name
+
+ run_pre_install_hooks
+
+ # Completely remove any previous gem files
+ FileUtils.rm_rf gem_dir
+
+ FileUtils.mkdir_p gem_dir
+
+ spec.loaded_from = spec_file
+
+ if @options[:install_as_default]
+ extract_bin
+ write_default_spec
+ else
+ extract_files
+
+ build_extensions
+ write_build_info_file
+ run_post_build_hooks
+
+ generate_bin
+ write_spec
+ write_cache_file
+ end
+
+ say spec.post_install_message unless spec.post_install_message.nil?
+
+ Gem::Installer.install_lock.synchronize { Gem::Specification.add_spec spec }
+
+ run_post_install_hooks
+
+ spec
+
+ # TODO This rescue is in the wrong place. What is raising this exception?
+ # move this rescue to around 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
+
+ location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
+
+ message = "post-build hook#{location} failed for #{spec.full_name}"
+ raise Gem::InstallError, message
+ end
+ end
+ end
+
+ def run_post_install_hooks # :nodoc:
+ Gem.post_install_hooks.each do |hook|
+ hook.call self
+ end
+ end
+
+ ##
+ #
+ # Return an Array of Specifications contained within the gem_home
+ # we'll be installing into.
+
+ def installed_specs
+ @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
+
+ ##
+ # Ensure that the dependency is satisfied by the current installation of
+ # gem. If it is not an exception is raised.
+ #
+ # spec :: Gem::Specification
+ # dependency :: Gem::Dependency
+
+ def ensure_dependency(spec, dependency)
+ unless installation_satisfies_dependency? dependency then
+ raise Gem::InstallError, "#{spec.name} requires #{dependency}"
+ end
+ true
+ end
+
+ ##
+ # True if the gems in the system satisfy +dependency+.
+
+ def installation_satisfies_dependency?(dependency)
+ return true if @options[:development] and dependency.type == :development
+ return true if installed_specs.detect { |s| dependency.matches_spec? s }
+ return false if @only_install_dir
+ not dependency.matching_specs.empty?
+ end
+
+ ##
+ # Unpacks the gem into the given directory.
+
+ def unpack(directory)
+ @gem_dir = directory
+ extract_files
+ end
+
+ ##
+ # The location of of the spec file that is installed.
+ #
+
+ def spec_file
+ File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
+ end
+
+ ##
+ # The location of of the default spec file for default gems.
+ #
+
+ def default_spec_file
+ File.join gem_home, "specifications/default", "#{spec.full_name}.gemspec"
+ end
+
+ ##
+ # Writes the .gemspec specification (in Ruby) to the gem home's
+ # specifications directory.
+
+ def write_spec
+ open spec_file, 'w' do |file|
+ spec.installed_by_version = Gem.rubygems_version
+
+ file.puts spec.to_ruby_for_cache
+
+ file.fsync rescue nil # for filesystems without fsync(2)
+ end
+ end
+
+ ##
+ # Writes the full .gemspec specification (in Ruby) to the gem home's
+ # specifications/default directory.
+
+ def write_default_spec
+ File.open(default_spec_file, "w") do |file|
+ file.puts spec.to_ruby
+ end
+ end
+
+ ##
+ # Creates windows .bat files for easy running of commands
+
+ def generate_windows_script(filename, bindir)
+ if Gem.win_platform? then
+ script_name = filename + ".bat"
+ script_path = File.join bindir, File.basename(script_name)
+ File.open script_path, 'w' do |file|
+ file.puts windows_stub_script(bindir, filename)
+ end
+
+ verbose script_path
+ end
+ end
+
+ def generate_bin # :nodoc:
+ return if spec.executables.nil? or spec.executables.empty?
+
+ Dir.mkdir @bin_dir unless File.exist? @bin_dir
+ raise Gem::FilePermissionError.new(@bin_dir) unless File.writable? @bin_dir
+
+ spec.executables.each do |filename|
+ filename.untaint
+ bin_path = File.join gem_dir, spec.bindir, filename
+
+ unless File.exist? bin_path then
+ # TODO change this to a more useful warning
+ warn "#{bin_path} maybe `gem pristine #{spec.name}` will fix it?"
+ next
+ end
+
+ mode = File.stat(bin_path).mode
+ FileUtils.chmod mode | 0111, bin_path unless (mode | 0111) == mode
+
+ check_executable_overwrite filename
+
+ if @wrappers then
+ generate_bin_script filename, @bin_dir
+ else
+ generate_bin_symlink filename, @bin_dir
+ end
+
+ end
+ end
+
+ ##
+ # Creates the scripts to run the applications in the gem.
+ #--
+ # The Windows script is generated in addition to the regular one due to a
+ # bug or misfeature in the Windows shell's pipe. See
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
+
+ def generate_bin_script(filename, bindir)
+ bin_script_path = File.join bindir, formatted_program_filename(filename)
+
+ FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
+
+ File.open bin_script_path, 'wb', 0755 do |file|
+ file.print app_script_text(filename)
+ end
+
+ verbose bin_script_path
+
+ generate_windows_script filename, bindir
+ end
+
+ ##
+ # Creates the symlinks to run the applications in the gem. Moves
+ # the symlink if the gem being installed has a newer version.
+
+ def generate_bin_symlink(filename, bindir)
+ if Gem.win_platform? then
+ alert_warning "Unable to use symlinks on Windows, installing wrapper"
+ generate_bin_script filename, bindir
+ return
+ end
+
+ src = File.join gem_dir, spec.bindir, filename
+ dst = File.join bindir, formatted_program_filename(filename)
+
+ if File.exist? dst then
+ if File.symlink? dst then
+ link = File.readlink(dst).split File::SEPARATOR
+ cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
+ return if spec.version < cur_version
+ end
+ File.unlink dst
+ end
+
+ FileUtils.symlink src, dst, :verbose => Gem.configuration.really_verbose
+ end
+
+ ##
+ # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
+ # 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)
+ ruby_name = RbConfig::CONFIG['ruby_install_name'] if @env_shebang
+ path = File.join gem_dir, spec.bindir, bin_file_name
+ first_line = File.open(path, "rb") {|file| file.gets}
+
+ if /\A#!/ =~ first_line then
+ # Preserve extra words on shebang line, like "-w". Thanks RPA.
+ shebang = first_line.sub(/\A\#!.*?ruby\S*((\s+\S+)+)/, "#!#{Gem.ruby}")
+ opts = $1
+ shebang.strip! # Avoid nasty ^M issues.
+ end
+
+ if which = Gem.configuration[:custom_shebang]
+ # replace bin_file_name with "ruby" to avoid endless loops
+ which = which.gsub(/ #{bin_file_name}$/," #{RbConfig::CONFIG['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}"
+ elsif opts then
+ "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
+ else
+ # Create a plain shebang line.
+ @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
+ "#!#{@env_path} #{ruby_name}"
+ end
+ end
+
+ ##
+ # Ensures the Gem::Specification written out for this gem is loadable upon
+ # installation.
+
+ def ensure_loadable_spec
+ ruby = spec.to_ruby_for_cache
+ ruby.untaint
+
+ begin
+ eval ruby
+ rescue StandardError, SyntaxError => e
+ raise Gem::InstallError,
+ "The specification for #{spec.full_name} is corrupt (#{e.class})"
+ end
+ end
+
+ def ensure_required_ruby_version_met # :nodoc:
+ if rrv = spec.required_ruby_version then
+ unless rrv.satisfied_by? Gem.ruby_version then
+ raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
+ end
+ end
+ end
+
+ def ensure_required_rubygems_version_met # :nodoc:
+ if rrgv = spec.required_rubygems_version then
+ unless rrgv.satisfied_by? Gem.rubygems_version then
+ raise Gem::InstallError,
+ "#{spec.name} requires RubyGems version #{rrgv}. " +
+ "Try 'gem update --system' to update RubyGems itself."
+ end
+ end
+ end
+
+ def ensure_dependencies_met # :nodoc:
+ deps = spec.runtime_dependencies
+ deps |= spec.development_dependencies if @development
+
+ deps.each do |dep_gem|
+ ensure_dependency spec, dep_gem
+ end
+ end
+
+ def process_options # :nodoc:
+ @options = {
+ :bin_dir => nil,
+ :env_shebang => false,
+ :force => false,
+ :only_install_dir => false
+ }.merge options
+
+ @env_shebang = options[:env_shebang]
+ @force = options[:force]
+ @install_dir = options[:install_dir]
+ @gem_home = options[:install_dir] || Gem.dir
+ @ignore_dependencies = options[:ignore_dependencies]
+ @format_executable = options[:format_executable]
+ @security_policy = options[:security_policy]
+ @wrappers = options[:wrappers]
+ @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]
+ @build_root = options[:build_root]
+
+ @build_args = options[:build_args] || Gem::Command.build_args
+
+ unless @build_root.nil?
+ require 'pathname'
+ @build_root = Pathname.new(@build_root).expand_path
+ @bin_dir = File.join(@build_root, options[:bin_dir] || Gem.bindir(@gem_home))
+ @gem_home = File.join(@build_root, @gem_home)
+ alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}"
+ end
+ end
+
+ def check_that_user_bin_dir_is_in_path # :nodoc:
+ user_bin_dir = @bin_dir || Gem.bindir(gem_home)
+ user_bin_dir = user_bin_dir.gsub(File::SEPARATOR, File::ALT_SEPARATOR) if
+ File::ALT_SEPARATOR
+
+ path = ENV['PATH']
+ if Gem.win_platform? then
+ path = path.downcase
+ user_bin_dir = user_bin_dir.downcase
+ end
+
+ path = path.split(File::PATH_SEPARATOR)
+
+ unless path.include? user_bin_dir then
+ unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV['HOME'], '~'))
+ unless self.class.path_warning then
+ alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
+ self.class.path_warning = true
+ end
+ end
+ end
+ end
+
+ def verify_gem_home(unpack = false) # :nodoc:
+ FileUtils.mkdir_p gem_home
+ raise Gem::FilePermissionError, gem_home unless
+ unpack or File.writable?(gem_home)
+ end
+
+ ##
+ # Return the text for an application file.
+
+ def app_script_text(bin_file_name)
+ return <<-TEXT
+#{shebang bin_file_name}
+#
+# This file was generated by RubyGems.
+#
+# The application '#{spec.name}' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+
+version = "#{Gem::Requirement.default}"
+
+if ARGV.first
+ str = ARGV.first
+ str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
+ if str =~ /\\A_(.*)_\\z/ and Gem::Version.correct?($1) then
+ version = $1
+ ARGV.shift
+ end
+end
+
+gem '#{spec.name}', version
+load Gem.bin_path('#{spec.name}', '#{bin_file_name}', version)
+TEXT
+ end
+
+ ##
+ # return the stub script text used to launch the true Ruby script
+
+ def windows_stub_script(bindir, bin_file_name)
+ ruby = Gem.ruby.chomp('"').tr(File::SEPARATOR, "\\")
+ return <<-TEXT
+@ECHO OFF
+IF NOT "%~f0" == "~f0" GOTO :WinNT
+@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+GOTO :EOF
+:WinNT
+@"#{ruby}" "%~dpn0" %*
+TEXT
+ end
+
+ ##
+ # Builds extensions. Valid types of extensions are extconf.rb files,
+ # configure scripts and rakefiles or mkrf_conf files.
+
+ def build_extensions
+ builder = Gem::Ext::Builder.new spec, @build_args
+
+ builder.build_extensions
+ end
+
+ ##
+ # Logs the build +output+ in +build_dir+, then raises Gem::Ext::BuildError.
+ #
+ # TODO: Delete this for RubyGems 3. It remains for API compatibility
+
+ def extension_build_error(build_dir, output, backtrace = nil) # :nodoc:
+ builder = Gem::Ext::Builder.new spec, @build_args
+
+ builder.build_error build_dir, output, backtrace
+ end
+
+ ##
+ # Reads the file index and extracts each file into the gem directory.
+ #
+ # Ensures that files can't be installed outside the gem directory.
+
+ def extract_files
+ @package.extract_files gem_dir
+ end
+
+ ##
+ # Extracts only the bin/ files from the gem into the gem directory.
+ # This is used by default gems to allow a gem-aware stub to function
+ # without the full gem installed.
+
+ def extract_bin
+ @package.extract_files gem_dir, "bin/*"
+ end
+
+ ##
+ # Prefix and suffix the program filename the same as ruby.
+
+ def formatted_program_filename(filename)
+ if @format_executable then
+ self.class.exec_format % File.basename(filename)
+ else
+ filename
+ end
+ end
+
+ ##
+ #
+ # Return the target directory where the gem is to be installed. This
+ # directory is not guaranteed to be populated.
+ #
+
+ def dir
+ gem_dir.to_s
+ end
+
+ ##
+ # Performs various checks before installing the gem such as the install
+ # repository is writable and its directories exist, required Ruby and
+ # rubygems versions are met and that dependencies are installed.
+ #
+ # Version and dependency checks are skipped if this install is forced.
+ #
+ # The dependent check will be skipped this install is ignoring dependencies.
+
+ def pre_install_checks
+ verify_gem_home options[:unpack]
+
+ # If we're forcing the install then disable security unless the security
+ # policy says that we only install signed gems.
+ @security_policy = nil if
+ @force and @security_policy and not @security_policy.only_signed
+
+ ensure_loadable_spec
+
+ if options[:install_as_default]
+ Gem.ensure_default_gem_subdirectories gem_home
+ else
+ Gem.ensure_gem_subdirectories gem_home
+ end
+
+ return true if @force
+
+ ensure_required_ruby_version_met
+ ensure_required_rubygems_version_met
+ ensure_dependencies_met unless @ignore_dependencies
+
+ true
+ end
+
+ ##
+ # Writes the file containing the arguments for building this gem's
+ # extensions.
+
+ def write_build_info_file
+ return if @build_args.empty?
+
+ build_info_dir = File.join gem_home, 'build_info'
+
+ FileUtils.mkdir_p build_info_dir
+
+ build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
+
+ open build_info_file, 'w' do |io|
+ @build_args.each do |arg|
+ io.puts arg
+ end
+ end
+ end
+
+ ##
+ # Writes the .gem file to the cache directory
+
+ def write_cache_file
+ cache_file = File.join gem_home, 'cache', spec.file_name
+
+ FileUtils.cp @gem, cache_file unless File.exist? cache_file
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/installer_test_case.rb b/jni/ruby/lib/rubygems/installer_test_case.rb
new file mode 100644
index 0000000..549de01
--- /dev/null
+++ b/jni/ruby/lib/rubygems/installer_test_case.rb
@@ -0,0 +1,193 @@
+require 'rubygems/test_case'
+require 'rubygems/installer'
+
+class Gem::Installer
+
+ ##
+ # 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
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :force
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :format
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :gem_home
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :env_shebang
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :ignore_dependencies
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :format_executable
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :security_policy
+
+ ##
+ # Available through requiring rubygems/installer_test_case
+
+ attr_writer :wrappers
+end
+
+##
+# A test case for Gem::Installer.
+
+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
+ super
+
+ @spec = quick_gem 'a' do |spec|
+ util_make_exec spec
+ end
+
+ @user_spec = quick_gem 'b' do |spec|
+ util_make_exec spec
+ end
+
+ util_build_gem @spec
+ util_build_gem @user_spec
+
+ @gem = @spec.cache_file
+ @user_gem = @user_spec.cache_file
+
+ @installer = util_installer @spec, @gemhome
+ @user_installer = util_installer @user_spec, Gem.user_dir, :user
+
+ Gem::Installer.path_warning = false
+ end
+
+ def util_gem_bindir spec = @spec # :nodoc:
+ # TODO: deprecate
+ spec.bin_dir
+ end
+
+ def util_gem_dir spec = @spec # :nodoc:
+ # TODO: deprecate
+ spec.gem_dir
+ end
+
+ ##
+ # The path where installed executables live
+
+ def util_inst_bindir
+ File.join @gemhome, "bin"
+ 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")
+ spec.executables = %w[executable]
+ spec.files << 'bin/executable'
+
+ exec_path = spec.bin_file "executable"
+ write_file exec_path do |io|
+ io.puts shebang
+ end
+
+ bin_path = File.join @tempdir, "bin", "executable"
+ write_file bin_path do |io|
+ io.puts shebang
+ 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
+ @spec.files << File.join('lib', 'code.rb')
+ @spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
+
+ Dir.chdir @tempdir do
+ FileUtils.mkdir_p 'bin'
+ FileUtils.mkdir_p 'lib'
+ FileUtils.mkdir_p File.join('ext', 'a')
+ File.open File.join('bin', 'executable'), 'w' do |f|
+ f.puts "raise 'ran executable'"
+ end
+ File.open File.join('lib', 'code.rb'), 'w' do |f| f.puts '1' end
+ File.open File.join('ext', 'a', 'mkrf_conf.rb'), 'w' do |f|
+ f << <<-EOF
+ File.open 'Rakefile', 'w' do |rf| rf.puts "task :default" end
+ EOF
+ end
+
+ use_ui ui do
+ FileUtils.rm_f @gem
+
+ @gem = Gem::Package.build @spec
+ end
+ end
+
+ @installer = Gem::Installer.new @gem
+ 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)
+ Gem::Installer.new(spec.cache_file,
+ :install_dir => gem_home,
+ :user_install => user)
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/local_remote_options.rb b/jni/ruby/lib/rubygems/local_remote_options.rb
new file mode 100644
index 0000000..db23d9f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/local_remote_options.rb
@@ -0,0 +1,148 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'uri'
+require 'rubygems'
+
+##
+# Mixin methods for local and remote Gem::Command options.
+
+module Gem::LocalRemoteOptions
+
+ ##
+ # Allows OptionParser to handle HTTP URIs.
+
+ def accept_uri_http
+ OptionParser.accept URI::HTTP do |value|
+ begin
+ uri = URI.parse value
+ rescue URI::InvalidURIError
+ raise OptionParser::InvalidArgument, value
+ end
+
+ unless ['http', 'https', 'file'].include?(uri.scheme)
+ raise OptionParser::InvalidArgument, value
+ end
+
+ value
+ end
+ end
+
+ ##
+ # Add local/remote options to the command line parser.
+
+ def add_local_remote_options
+ add_option(:"Local/Remote", '-l', '--local',
+ 'Restrict operations to the LOCAL domain') do |value, options|
+ options[:domain] = :local
+ end
+
+ add_option(:"Local/Remote", '-r', '--remote',
+ 'Restrict operations to the REMOTE domain') do |value, options|
+ options[:domain] = :remote
+ end
+
+ add_option(:"Local/Remote", '-b', '--both',
+ 'Allow LOCAL and REMOTE operations') do |value, options|
+ options[:domain] = :both
+ end
+
+ add_bulk_threshold_option
+ add_clear_sources_option
+ add_source_option
+ add_proxy_option
+ add_update_sources_option
+ end
+
+ ##
+ # Add the --bulk-threshold option
+
+ def add_bulk_threshold_option
+ add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
+ "Threshold for switching to bulk",
+ "synchronization (default #{Gem.configuration.bulk_threshold})") do
+ |value, options|
+ Gem.configuration.bulk_threshold = value.to_i
+ end
+ end
+
+ ##
+ # Add the --clear-sources option
+
+ def add_clear_sources_option
+ add_option(:"Local/Remote", '--clear-sources',
+ 'Clear the gem sources') do |value, options|
+
+ Gem.sources = nil
+ options[:sources_cleared] = true
+ end
+ end
+
+ ##
+ # Add the --http-proxy option
+
+ def add_proxy_option
+ accept_uri_http
+
+ add_option(:"Local/Remote", '-p', '--[no-]http-proxy [URL]', URI::HTTP,
+ 'Use HTTP proxy for remote operations') do |value, options|
+ options[:http_proxy] = (value == false) ? :no_proxy : value
+ Gem.configuration[:http_proxy] = options[:http_proxy]
+ end
+ end
+
+ ##
+ # Add the --source option
+
+ def add_source_option
+ accept_uri_http
+
+ add_option(:"Local/Remote", '-s', '--source URL', URI::HTTP,
+ 'Append URL to list of remote gem sources') do |source, options|
+
+ source << '/' if source !~ /\/\z/
+
+ if options.delete :sources_cleared then
+ Gem.sources = [source]
+ else
+ Gem.sources << source unless Gem.sources.include?(source)
+ end
+ end
+ end
+
+ ##
+ # Add the --update-sources option
+
+ def add_update_sources_option
+ add_option(:Deprecated, '-u', '--[no-]update-sources',
+ 'Update local source cache') do |value, options|
+ Gem.configuration.update_sources = value
+ end
+ end
+
+ ##
+ # Is fetching of local and remote information enabled?
+
+ def both?
+ options[:domain] == :both
+ end
+
+ ##
+ # Is local fetching enabled?
+
+ def local?
+ options[:domain] == :local || options[:domain] == :both
+ end
+
+ ##
+ # Is remote fetching enabled?
+
+ def remote?
+ options[:domain] == :remote || options[:domain] == :both
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/mock_gem_ui.rb b/jni/ruby/lib/rubygems/mock_gem_ui.rb
new file mode 100644
index 0000000..76a9389
--- /dev/null
+++ b/jni/ruby/lib/rubygems/mock_gem_ui.rb
@@ -0,0 +1,88 @@
+require 'stringio'
+require 'rubygems/user_interaction'
+
+##
+# This Gem::StreamUI subclass records input and output to StringIO for
+# retrieval during tests.
+
+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
+ attr_reader :exit_code
+
+ def initialize exit_code
+ super
+ @exit_code = exit_code
+ end
+ end
+ class SystemExitException < RuntimeError; end
+
+ module TTY
+
+ attr_accessor :tty
+
+ def tty?()
+ @tty = true unless defined?(@tty)
+ @tty
+ end
+
+ def noecho
+ yield self
+ end
+ end
+
+ def initialize(input = "")
+ ins = StringIO.new input
+ outs = StringIO.new
+ errs = StringIO.new
+
+ ins.extend TTY
+ outs.extend TTY
+ errs.extend TTY
+
+ super ins, outs, errs, true
+
+ @terminated = false
+ end
+
+ def ask question
+ raise InputEOFError, question if @ins.eof?
+
+ super
+ end
+
+ def input
+ @ins.string
+ end
+
+ def output
+ @outs.string
+ end
+
+ def error
+ @errs.string
+ end
+
+ def terminated?
+ @terminated
+ end
+
+ def terminate_interaction(status=0)
+ @terminated = true
+
+ raise TermError, status if status != 0
+ raise SystemExitException
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/name_tuple.rb b/jni/ruby/lib/rubygems/name_tuple.rb
new file mode 100644
index 0000000..60323db
--- /dev/null
+++ b/jni/ruby/lib/rubygems/name_tuple.rb
@@ -0,0 +1,123 @@
+##
+#
+# 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
+
+ ##
+ # Returns the full name (name-version) of this Gem. Platform information is
+ # included if it is not the default Ruby platform. This mimics the behavior
+ # of Gem::Specification#full_name.
+
+ def full_name
+ case @platform
+ when nil, 'ruby', ''
+ "#{@name}-#{@version}"
+ else
+ "#{@name}-#{@version}-#{@platform}"
+ end.untaint
+ 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
+ "#{full_name}.gemspec"
+ end
+
+ ##
+ # Convert back to the [name, version, platform] tuple
+
+ def to_a
+ [@name, @version, @platform]
+ end
+
+ def inspect # :nodoc:
+ "#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>"
+ end
+
+ alias to_s inspect # :nodoc:
+
+ def <=> other
+ [@name, @version, @platform == Gem::Platform::RUBY ? -1 : 1] <=>
+ [other.name, other.version,
+ other.platform == Gem::Platform::RUBY ? -1 : 1]
+ 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
diff --git a/jni/ruby/lib/rubygems/package.rb b/jni/ruby/lib/rubygems/package.rb
new file mode 100644
index 0000000..417b34b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package.rb
@@ -0,0 +1,613 @@
+# -*- coding: utf-8 -*-
+#--
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# 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/user_interaction'
+require 'zlib'
+
+class Gem::Package
+
+ include Gem::UserInteraction
+
+ class Error < Gem::Exception; end
+
+ class FormatError < Error
+ attr_reader :path
+
+ def initialize message, source = nil
+ if source
+ @path = source.path
+
+ message << " in #{path}" if path
+ end
+
+ super message
+ 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
+
+ class TarInvalidError < Error; end
+
+
+ attr_accessor :build_time # :nodoc:
+
+ ##
+ # 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
+
+ ##
+ # Creates a new Gem::Package for the file at +gem+. +gem+ can also be
+ # provided as an IO object.
+ #
+ # If +gem+ is an existing file in the old format a Gem::Package::Old will be
+ # returned.
+
+ def self.new gem
+ gem = if gem.is_a?(Gem::Package::Source)
+ gem
+ elsif gem.respond_to? :read
+ Gem::Package::IOSource.new gem
+ else
+ Gem::Package::FileSource.new gem
+ end
+
+ return super(gem) unless Gem::Package == self
+ return super unless gem.present?
+
+ return super unless gem.start
+ return super unless gem.start.include? 'MD5SUM ='
+
+ Gem::Package::Old.new gem
+ end
+
+ ##
+ # Creates a new package that will read or write to the file +gem+.
+
+ 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
+
+ next unless 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
+
+ ##
+ # 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
+ Gem.load_yaml
+ require 'rubygems/security'
+
+ @spec.mark_version
+ @spec.validate unless skip_validation
+
+ setup_signer
+
+ @gem.with_write_io 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 = []
+
+ @gem.with_read_io 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:
+ algorithms = if @checksums then
+ @checksums.keys
+ else
+ [Gem::Security::DIGEST_NAME].compact
+ end
+
+ algorithms.each do |algorithm|
+ digester =
+ if defined?(OpenSSL::Digest) then
+ OpenSSL::Digest.new algorithm
+ else
+ Digest.const_get(algorithm).new
+ end
+
+ 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+
+ #
+ # If +pattern+ is specified, only entries matching that glob will be
+ # extracted.
+
+ def extract_files destination_dir, pattern = "*"
+ verify unless @spec
+
+ FileUtils.mkdir_p destination_dir
+
+ @gem.with_read_io 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, pattern
+
+ 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.
+ #
+ # If +pattern+ is specified, only entries matching that glob will be
+ # extracted.
+
+ def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
+ open_tar_gz io do |tar|
+ tar.each do |entry|
+ next unless File.fnmatch pattern, entry.full_name, File::FNM_DOTMATCH
+
+ destination = install_location entry.full_name, destination_dir
+
+ FileUtils.rm_rf destination
+
+ mkdir_options = {}
+ mkdir_options[:mode] = entry.header.mode if entry.directory?
+ mkdir =
+ if entry.directory? then
+ destination
+ else
+ File.dirname destination
+ end
+
+ FileUtils.mkdir_p mkdir, mkdir_options
+
+ open destination, 'wb', entry.header.mode do |out|
+ out.write entry.read
+ end if entry.file?
+
+ verbose destination
+ 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_dir = File.realpath destination_dir if
+ File.respond_to? :realpath
+ destination_dir = File.expand_path destination_dir
+
+ 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.method(:wrap).arity != 1
+
+ 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
+ passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ if @spec.signing_key then
+ @signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain, passphrase
+ @spec.signing_key = nil
+ @spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
+ else
+ @signer = Gem::Security::Signer.new nil, nil, passphrase
+ @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
+
+ @gem.with_read_io 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 Gem::Security::Exception
+ @spec = nil
+ @files = []
+ raise
+ 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 +entry+ in a .gem file.
+
+ def verify_entry entry
+ file_name = entry.full_name
+ @files << file_name
+
+ case file_name
+ when /\.sig$/ then
+ @signatures[$`] = entry.read if @security_policy
+ return
+ else
+ digest entry
+ end
+
+ case file_name
+ when /^metadata(.gz)?$/ then
+ load_spec entry
+ when 'data.tar.gz' then
+ verify_gz entry
+ end
+ rescue => e
+ message = "package is corrupt, exception while verifying: " +
+ "#{e.message} (#{e.class})"
+ raise Gem::Package::FormatError.new message, @gem
+ end
+
+ ##
+ # Verifies the files of the +gem+
+
+ def verify_files gem
+ gem.each do |entry|
+ verify_entry entry
+ 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
+
+require 'rubygems/package/digest_io'
+require 'rubygems/package/source'
+require 'rubygems/package/file_source'
+require 'rubygems/package/io_source'
+require 'rubygems/package/old'
+require 'rubygems/package/tar_header'
+require 'rubygems/package/tar_reader'
+require 'rubygems/package/tar_reader/entry'
+require 'rubygems/package/tar_writer'
+
diff --git a/jni/ruby/lib/rubygems/package/digest_io.rb b/jni/ruby/lib/rubygems/package/digest_io.rb
new file mode 100644
index 0000000..f8bde0f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/digest_io.rb
@@ -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
+
diff --git a/jni/ruby/lib/rubygems/package/file_source.rb b/jni/ruby/lib/rubygems/package/file_source.rb
new file mode 100644
index 0000000..85316f6
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/file_source.rb
@@ -0,0 +1,33 @@
+##
+# The primary source of gems is a file on disk, including all usages
+# internal to rubygems.
+#
+# This is a private class, do not depend on it directly. Instead, pass a path
+# object to `Gem::Package.new`.
+
+class Gem::Package::FileSource < Gem::Package::Source # :nodoc: all
+
+ attr_reader :path
+
+ def initialize path
+ @path = path
+ end
+
+ def start
+ @start ||= File.read path, 20
+ end
+
+ def present?
+ File.exist? path
+ end
+
+ def with_write_io &block
+ open path, 'wb', &block
+ end
+
+ def with_read_io &block
+ open path, 'rb', &block
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package/io_source.rb b/jni/ruby/lib/rubygems/package/io_source.rb
new file mode 100644
index 0000000..f89593d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/io_source.rb
@@ -0,0 +1,45 @@
+##
+# Supports reading and writing gems from/to a generic IO object. This is
+# useful for other applications built on top of rubygems, such as
+# rubygems.org.
+#
+# This is a private class, do not depend on it directly. Instead, pass an IO
+# object to `Gem::Package.new`.
+
+class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all
+
+ attr_reader :io
+
+ def initialize io
+ @io = io
+ end
+
+ def start
+ @start ||= begin
+ if io.pos > 0
+ raise Gem::Package::Error, "Cannot read start unless IO is at start"
+ end
+
+ value = io.read 20
+ io.rewind
+ value
+ end
+ end
+
+ def present?
+ true
+ end
+
+ def with_read_io
+ yield io
+ end
+
+ def with_write_io
+ yield io
+ end
+
+ def path
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package/old.rb b/jni/ruby/lib/rubygems/package/old.rb
new file mode 100644
index 0000000..d7b228d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/old.rb
@@ -0,0 +1,178 @@
+#--
+# 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
+
+ @contents = nil
+ @gem = gem
+ @security_policy = nil
+ @spec = nil
+ end
+
+ ##
+ # A list of file names contained in this gem
+
+ def contents
+ verify
+
+ return @contents if @contents
+
+ @gem.with_read_io 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
+ verify
+
+ errstr = "Error reading files from gem"
+
+ @gem.with_read_io 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
+
+ verbose destination
+ 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
+ verify
+
+ return @spec if @spec
+
+ yaml = ''
+
+ @gem.with_read_io do |io|
+ skip_ruby io
+ read_until_dashes io do |line|
+ yaml << line
+ end
+ end
+
+ yaml_error = if RUBY_VERSION < '1.9' then
+ YAML::ParseError
+ elsif YAML.const_defined?(:ENGINE) && YAML::ENGINE.yamler == 'syck' then
+ YAML::ParseError
+ else
+ YAML::SyntaxError
+ end
+
+ begin
+ @spec = Gem::Specification.from_yaml yaml
+ rescue yaml_error
+ raise Gem::Exception, "Failed to parse gem specification out of gem file"
+ end
+ rescue ArgumentError
+ raise Gem::Exception, "Failed to parse gem specification out of gem file"
+ end
+
+ ##
+ # Raises an exception if a security policy that verifies data is active.
+ # Old format gems cannot be verified as signed.
+
+ def verify
+ return true unless @security_policy
+
+ raise Gem::Security::Exception,
+ 'old format gems do not contain signatures and cannot be verified' if
+ @security_policy.verify_data
+
+ true
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package/source.rb b/jni/ruby/lib/rubygems/package/source.rb
new file mode 100644
index 0000000..1f18d47
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/source.rb
@@ -0,0 +1,3 @@
+class Gem::Package::Source # :nodoc:
+end
+
diff --git a/jni/ruby/lib/rubygems/package/tar_header.rb b/jni/ruby/lib/rubygems/package/tar_header.rb
new file mode 100644
index 0000000..f9ab13a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/tar_header.rb
@@ -0,0 +1,229 @@
+# -*- coding: utf-8 -*-
+#--
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#++
+
+##
+#--
+# struct tarfile_entry_posix {
+# char name[100]; # ASCII + (Z unless filled)
+# char mode[8]; # 0 padded, octal, null
+# char uid[8]; # ditto
+# char gid[8]; # ditto
+# char size[12]; # 0 padded, octal, null
+# char mtime[12]; # 0 padded, octal, null
+# char checksum[8]; # 0 padded, octal, null, space
+# char typeflag[1]; # file: "0" dir: "5"
+# char linkname[100]; # ASCII + (Z unless filled)
+# char magic[6]; # "ustar\0"
+# char version[2]; # "00"
+# char uname[32]; # ASCIIZ
+# char gname[32]; # ASCIIZ
+# char devmajor[8]; # 0 padded, octal, null
+# char devminor[8]; # o padded, octal, null
+# char prefix[155]; # ASCII + (Z unless filled)
+# };
+#++
+# A header for a tar file
+
+class Gem::Package::TarHeader
+
+ ##
+ # Fields in the tar header
+
+ FIELDS = [
+ :checksum,
+ :devmajor,
+ :devminor,
+ :gid,
+ :gname,
+ :linkname,
+ :magic,
+ :mode,
+ :mtime,
+ :name,
+ :prefix,
+ :size,
+ :typeflag,
+ :uid,
+ :uname,
+ :version,
+ ]
+
+ ##
+ # Pack format for a tar header
+
+ PACK_FORMAT = 'a100' + # name
+ 'a8' + # mode
+ 'a8' + # uid
+ 'a8' + # gid
+ 'a12' + # size
+ 'a12' + # mtime
+ 'a7a' + # chksum
+ 'a' + # typeflag
+ 'a100' + # linkname
+ 'a6' + # magic
+ 'a2' + # version
+ 'a32' + # uname
+ 'a32' + # gname
+ 'a8' + # devmajor
+ 'a8' + # devminor
+ 'a155' # prefix
+
+ ##
+ # Unpack format for a tar header
+
+ UNPACK_FORMAT = 'A100' + # name
+ 'A8' + # mode
+ 'A8' + # uid
+ 'A8' + # gid
+ 'A12' + # size
+ 'A12' + # mtime
+ 'A8' + # checksum
+ 'A' + # typeflag
+ 'A100' + # linkname
+ 'A6' + # magic
+ 'A2' + # version
+ 'A32' + # uname
+ 'A32' + # gname
+ 'A8' + # devmajor
+ 'A8' + # devminor
+ 'A155' # prefix
+
+ attr_reader(*FIELDS)
+
+ ##
+ # Creates a tar header from IO +stream+
+
+ def self.from(stream)
+ header = stream.read 512
+ empty = (header == "\0" * 512)
+
+ fields = header.unpack UNPACK_FORMAT
+
+ 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
+
+ ##
+ # Creates a new TarHeader using +vals+
+
+ def initialize(vals)
+ unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
+ raise ArgumentError, ":name, :size, :prefix and :mode required"
+ end
+
+ vals[:uid] ||= 0
+ vals[:gid] ||= 0
+ vals[:mtime] ||= 0
+ vals[:checksum] ||= ""
+ vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty?
+ vals[:magic] ||= "ustar"
+ vals[:version] ||= "00"
+ vals[:uname] ||= "wheel"
+ vals[:gname] ||= "wheel"
+ vals[:devmajor] ||= 0
+ vals[:devminor] ||= 0
+
+ FIELDS.each do |name|
+ instance_variable_set "@#{name}", vals[name]
+ end
+
+ @empty = vals[:empty]
+ end
+
+ ##
+ # Is the tar entry empty?
+
+ def empty?
+ @empty
+ end
+
+ def ==(other) # :nodoc:
+ self.class === other and
+ @checksum == other.checksum and
+ @devmajor == other.devmajor and
+ @devminor == other.devminor and
+ @gid == other.gid and
+ @gname == other.gname and
+ @linkname == other.linkname and
+ @magic == other.magic and
+ @mode == other.mode and
+ @mtime == other.mtime and
+ @name == other.name and
+ @prefix == other.prefix and
+ @size == other.size and
+ @typeflag == other.typeflag and
+ @uid == other.uid and
+ @uname == other.uname and
+ @version == other.version
+ end
+
+ def to_s # :nodoc:
+ update_checksum
+ header
+ end
+
+ ##
+ # Updates the TarHeader's checksum
+
+ def update_checksum
+ header = header " " * 8
+ @checksum = oct calculate_checksum(header), 6
+ end
+
+ private
+
+ def calculate_checksum(header)
+ header.unpack("C*").inject { |a, b| a + b }
+ end
+
+ def header(checksum = @checksum)
+ header = [
+ name,
+ oct(mode, 7),
+ oct(uid, 7),
+ oct(gid, 7),
+ oct(size, 11),
+ oct(mtime, 11),
+ checksum,
+ " ",
+ typeflag,
+ linkname,
+ magic,
+ oct(version, 2),
+ uname,
+ gname,
+ oct(devmajor, 7),
+ oct(devminor, 7),
+ prefix
+ ]
+
+ header = header.pack PACK_FORMAT
+
+ header << ("\0" * ((512 - header.size) % 512))
+ end
+
+ def oct(num, len)
+ "%0#{len}o" % num
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package/tar_reader.rb b/jni/ruby/lib/rubygems/package/tar_reader.rb
new file mode 100644
index 0000000..e257fdd
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/tar_reader.rb
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+#--
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#++
+
+##
+# TarReader reads tar files and allows iteration over their items
+
+class Gem::Package::TarReader
+
+ include Enumerable
+
+ ##
+ # Raised if the tar IO is not seekable
+
+ class UnexpectedEOF < StandardError; end
+
+ ##
+ # Creates a new TarReader on +io+ and yields it to the block, if given.
+
+ def self.new(io)
+ reader = super
+
+ return reader unless block_given?
+
+ begin
+ yield reader
+ ensure
+ reader.close
+ end
+
+ nil
+ end
+
+ ##
+ # Creates a new tar file reader on +io+ which needs to respond to #pos,
+ # #eof?, #read, #getc and #pos=
+
+ def initialize(io)
+ @io = io
+ @init_pos = io.pos
+ end
+
+ ##
+ # Close the tar file
+
+ def close
+ end
+
+ ##
+ # Iterates over files in the tarball yielding each entry
+
+ def each
+ return enum_for __method__ unless block_given?
+
+ until @io.eof? do
+ header = Gem::Package::TarHeader.from @io
+ return if header.empty?
+
+ entry = Gem::Package::TarReader::Entry.new header, @io
+ size = entry.header.size
+
+ yield entry
+
+ skip = (512 - (size % 512)) % 512
+ pending = size - entry.bytes_read
+
+ begin
+ # avoid reading...
+ @io.seek pending, IO::SEEK_CUR
+ pending = 0
+ rescue Errno::EINVAL, NameError
+ while pending > 0 do
+ bytes_read = @io.read([pending, 4096].min).size
+ raise UnexpectedEOF if @io.eof?
+ pending -= bytes_read
+ end
+ end
+
+ @io.read skip # discard trailing zeros
+
+ # make sure nobody can use #read, #getc or #rewind anymore
+ entry.close
+ end
+ end
+
+ alias each_entry each
+
+ ##
+ # NOTE: Do not call #rewind during #each
+
+ def rewind
+ if @init_pos == 0 then
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :rewind
+ @io.rewind
+ else
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
+ @io.pos = @init_pos
+ 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
+
+require 'rubygems/package/tar_reader/entry'
+
diff --git a/jni/ruby/lib/rubygems/package/tar_reader/entry.rb b/jni/ruby/lib/rubygems/package/tar_reader/entry.rb
new file mode 100644
index 0000000..737c763
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/tar_reader/entry.rb
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+##
+# Class for reading entries out of a tar file
+
+class Gem::Package::TarReader::Entry
+
+ ##
+ # Header for this tar entry
+
+ attr_reader :header
+
+ ##
+ # Creates a new tar entry for +header+ that will be read from +io+
+
+ def initialize(header, io)
+ @closed = false
+ @header = header
+ @io = io
+ @orig_pos = @io.pos
+ @read = 0
+ end
+
+ def check_closed # :nodoc:
+ raise IOError, "closed #{self.class}" if closed?
+ end
+
+ ##
+ # Number of bytes read out of the tar entry
+
+ def bytes_read
+ @read
+ end
+
+ ##
+ # Closes the tar entry
+
+ def close
+ @closed = true
+ end
+
+ ##
+ # Is the tar entry closed?
+
+ def closed?
+ @closed
+ end
+
+ ##
+ # Are we at the end of the tar entry?
+
+ def eof?
+ check_closed
+
+ @read >= @header.size
+ end
+
+ ##
+ # Full name of the tar entry
+
+ def full_name
+ if @header.prefix != "" then
+ File.join @header.prefix, @header.name
+ else
+ @header.name
+ end
+ rescue ArgumentError => e
+ raise unless e.message == 'string contains null byte'
+ raise Gem::Package::TarInvalidError,
+ 'tar is corrupt, name contains null byte'
+ end
+
+ ##
+ # Read one byte from the tar entry
+
+ def getc
+ check_closed
+
+ return nil if @read >= @header.size
+
+ ret = @io.getc
+ @read += 1 if ret
+
+ ret
+ end
+
+ ##
+ # Is this tar entry a directory?
+
+ def directory?
+ @header.typeflag == "5"
+ end
+
+ ##
+ # Is this tar entry a file?
+
+ def file?
+ @header.typeflag == "0"
+ end
+
+ ##
+ # The position in the tar entry
+
+ def pos
+ check_closed
+
+ bytes_read
+ end
+
+ ##
+ # Reads +len+ bytes from the tar file entry, or the rest of the entry if
+ # nil
+
+ def read(len = nil)
+ check_closed
+
+ return nil if @read >= @header.size
+
+ len ||= @header.size - @read
+ max_read = [len, @header.size - @read].min
+
+ ret = @io.read max_read
+ @read += ret.size
+
+ ret
+ end
+
+ alias readpartial read # :nodoc:
+
+ ##
+ # Rewinds to the beginning of the tar file entry
+
+ def rewind
+ check_closed
+
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
+
+ @io.pos = @orig_pos
+ @read = 0
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package/tar_test_case.rb b/jni/ruby/lib/rubygems/package/tar_test_case.rb
new file mode 100644
index 0000000..5253e32
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/tar_test_case.rb
@@ -0,0 +1,137 @@
+require 'rubygems/test_case'
+require 'rubygems/package'
+
+##
+# A test case for Gem::Package::Tar* classes
+
+class Gem::Package::TarTestCase < Gem::TestCase
+
+ def ASCIIZ(str, length)
+ str + "\0" * (length - str.length)
+ end
+
+ def SP(s)
+ s + " "
+ end
+
+ def SP_Z(s)
+ s + " \0"
+ end
+
+ def Z(s)
+ s + "\0"
+ end
+
+ def assert_headers_equal(expected, actual)
+ expected = expected.to_s unless String === expected
+ actual = actual.to_s unless String === actual
+
+ fields = %w[
+ name 100
+ mode 8
+ uid 8
+ gid 8
+ size 12
+ mtime 12
+ checksum 8
+ typeflag 1
+ linkname 100
+ magic 6
+ version 2
+ uname 32
+ gname 32
+ devmajor 8
+ devminor 8
+ prefix 155
+ ]
+
+ offset = 0
+
+ until fields.empty? do
+ name = fields.shift
+ length = fields.shift.to_i
+
+ if name == "checksum" then
+ chksum_off = offset
+ offset += length
+ next
+ end
+
+ assert_equal expected[offset, length], actual[offset, length],
+ "Field #{name} of the tar header differs."
+
+ offset += length
+ end
+
+ assert_equal expected[chksum_off, 8], actual[chksum_off, 8]
+ end
+
+ def calc_checksum(header)
+ sum = header.unpack("C*").inject{|s,a| s + a}
+ SP(Z(to_oct(sum, 6)))
+ end
+
+ def header(type, fname, dname, length, mode, mtime, checksum = nil)
+ checksum ||= " " * 8
+
+ arr = [ # struct tarfile_entry_posix
+ ASCIIZ(fname, 100), # char name[100]; ASCII + (Z unless filled)
+ Z(to_oct(mode, 7)), # char mode[8]; 0 padded, octal null
+ Z(to_oct(0, 7)), # char uid[8]; ditto
+ Z(to_oct(0, 7)), # char gid[8]; ditto
+ Z(to_oct(length, 11)), # char size[12]; 0 padded, octal, null
+ Z(to_oct(mtime, 11)), # char mtime[12]; 0 padded, octal, null
+ checksum, # char checksum[8]; 0 padded, octal, null, space
+ type, # char typeflag[1]; file: "0" dir: "5"
+ "\0" * 100, # char linkname[100]; ASCII + (Z unless filled)
+ "ustar\0", # char magic[6]; "ustar\0"
+ "00", # char version[2]; "00"
+ ASCIIZ("wheel", 32), # char uname[32]; ASCIIZ
+ ASCIIZ("wheel", 32), # char gname[32]; ASCIIZ
+ Z(to_oct(0, 7)), # char devmajor[8]; 0 padded, octal, null
+ Z(to_oct(0, 7)), # char devminor[8]; 0 padded, octal, null
+ ASCIIZ(dname, 155) # char prefix[155]; ASCII + (Z unless filled)
+ ]
+
+ format = "C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155"
+ h = if RUBY_VERSION >= "1.9" then
+ arr.join
+ else
+ arr = arr.join("").split(//).map{|x| x[0]}
+ arr.pack format
+ end
+ ret = h + "\0" * (512 - h.size)
+ assert_equal(512, ret.size)
+ ret
+ end
+
+ def tar_dir_header(name, prefix, mode, mtime)
+ h = header("5", name, prefix, 0, mode, mtime)
+ checksum = calc_checksum(h)
+ header("5", name, prefix, 0, mode, mtime, checksum)
+ end
+
+ def tar_file_header(fname, dname, mode, length, mtime)
+ h = header("0", fname, dname, length, mode, mtime)
+ checksum = calc_checksum(h)
+ header("0", fname, dname, length, mode, mtime, checksum)
+ end
+
+ def to_oct(n, pad_size)
+ "%0#{pad_size}o" % n
+ end
+
+ def util_entry(tar)
+ io = TempIO.new tar
+
+ header = Gem::Package::TarHeader.from io
+
+ Gem::Package::TarReader::Entry.new header, io
+ end
+
+ def util_dir_entry
+ util_entry tar_dir_header("foo", "bar", 0, Time.now)
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package/tar_writer.rb b/jni/ruby/lib/rubygems/package/tar_writer.rb
new file mode 100644
index 0000000..dfd6357
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package/tar_writer.rb
@@ -0,0 +1,326 @@
+# -*- coding: utf-8 -*-
+#--
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#++
+
+require 'digest'
+
+##
+# Allows writing of tar files
+
+class Gem::Package::TarWriter
+
+ class FileOverflow < StandardError; end
+
+ ##
+ # IO wrapper that allows writing a limited amount of data
+
+ class BoundedStream
+
+ ##
+ # Maximum number of bytes that can be written
+
+ attr_reader :limit
+
+ ##
+ # Number of bytes written
+
+ attr_reader :written
+
+ ##
+ # Wraps +io+ and allows up to +limit+ bytes to be written
+
+ def initialize(io, limit)
+ @io = io
+ @limit = limit
+ @written = 0
+ end
+
+ ##
+ # Writes +data+ onto the IO, raising a FileOverflow exception if the
+ # number of bytes will be more than #limit
+
+ def write(data)
+ if data.bytesize + @written > @limit
+ raise FileOverflow, "You tried to feed more data than fits in the file."
+ end
+ @io.write data
+ @written += data.bytesize
+ data.bytesize
+ end
+
+ end
+
+ ##
+ # IO wrapper that provides only #write
+
+ class RestrictedStream
+
+ ##
+ # Creates a new RestrictedStream wrapping +io+
+
+ def initialize(io)
+ @io = io
+ end
+
+ ##
+ # Writes +data+ onto the IO
+
+ def write(data)
+ @io.write data
+ end
+
+ end
+
+ ##
+ # Creates a new TarWriter, yielding it if a block is given
+
+ def self.new(io)
+ writer = super
+
+ return writer unless block_given?
+
+ begin
+ yield writer
+ ensure
+ writer.close
+ end
+
+ nil
+ end
+
+ ##
+ # Creates a new TarWriter that will write to +io+
+
+ def initialize(io)
+ @io = io
+ @closed = false
+ end
+
+ ##
+ # Adds file +name+ with permissions +mode+, and yields an IO for writing the
+ # file to
+
+ def add_file(name, mode) # :yields: io
+ check_closed
+
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
+
+ name, prefix = split_name name
+
+ init_pos = @io.pos
+ @io.write "\0" * 512 # placeholder for the header
+
+ yield RestrictedStream.new(@io) if block_given?
+
+ size = @io.pos - init_pos - 512
+
+ remainder = (512 - (size % 512)) % 512
+ @io.write "\0" * remainder
+
+ final_pos = @io.pos
+ @io.pos = init_pos
+
+ header = Gem::Package::TarHeader.new :name => name, :mode => mode,
+ :size => size, :prefix => prefix,
+ :mtime => Time.now
+
+ @io.write header
+ @io.pos = final_pos
+
+ self
+ 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 =
+ if digest.respond_to? :name then
+ digest.name
+ else
+ /::([^:]+)$/ =~ digest_algorithm.name
+ $1
+ end
+
+ [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,
+ Digest::SHA512,
+ ].compact.uniq
+
+ digests = add_file_digest name, mode, digest_algorithms do |io|
+ yield io
+ end
+
+ signature_digest = digests.values.compact.find do |digest|
+ digest_name =
+ if digest.respond_to? :name then
+ digest.name
+ else
+ /::([^:]+)$/ =~ digest.class.name
+ $1
+ end
+
+ digest_name == signer.digest_name
+ end
+
+ if signer.key then
+ signature = signer.sign signature_digest.digest
+
+ add_file_simple "#{name}.sig", 0444, signature.length do |io|
+ io.write signature
+ end
+ end
+
+ digests
+ end
+
+ ##
+ # Add file +name+ with permissions +mode+ +size+ bytes long. Yields an IO
+ # to write the file to.
+
+ def add_file_simple(name, mode, size) # :yields: io
+ check_closed
+
+ name, prefix = split_name name
+
+ header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
+ :size => size, :prefix => prefix,
+ :mtime => Time.now).to_s
+
+ @io.write header
+ os = BoundedStream.new @io, size
+
+ yield os if block_given?
+
+ min_padding = size - os.written
+ @io.write("\0" * min_padding)
+
+ remainder = (512 - (size % 512)) % 512
+ @io.write("\0" * remainder)
+
+ self
+ end
+
+ ##
+ # Raises IOError if the TarWriter is closed
+
+ def check_closed
+ raise IOError, "closed #{self.class}" if closed?
+ end
+
+ ##
+ # Closes the TarWriter
+
+ def close
+ check_closed
+
+ @io.write "\0" * 1024
+ flush
+
+ @closed = true
+ end
+
+ ##
+ # Is the TarWriter closed?
+
+ def closed?
+ @closed
+ end
+
+ ##
+ # Flushes the TarWriter's IO
+
+ def flush
+ check_closed
+
+ @io.flush if @io.respond_to? :flush
+ end
+
+ ##
+ # Creates a new directory in the tar file +name+ with +mode+
+
+ def mkdir(name, mode)
+ check_closed
+
+ name, prefix = split_name(name)
+
+ header = Gem::Package::TarHeader.new :name => name, :mode => mode,
+ :typeflag => "5", :size => 0,
+ :prefix => prefix,
+ :mtime => Time.now
+
+ @io.write header
+
+ self
+ end
+
+ ##
+ # Splits +name+ into a name and prefix that can fit in the TarHeader
+
+ def split_name(name) # :nodoc:
+ if name.bytesize > 256
+ raise Gem::Package::TooLongFileName.new("File \"#{name}\" has a too long path (should be 256 or less)")
+ end
+
+ if name.bytesize <= 100 then
+ prefix = ""
+ else
+ parts = name.split(/\//)
+ newname = parts.pop
+ nxt = ""
+
+ loop do
+ nxt = parts.pop
+ break if newname.bytesize + 1 + nxt.bytesize > 100
+ newname = nxt + "/" + newname
+ end
+
+ prefix = (parts + [nxt]).join "/"
+ name = newname
+
+ if name.bytesize > 100
+ raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long name (should be 100 or less)")
+ end
+
+ if prefix.bytesize > 155 then
+ raise Gem::Package::TooLongFileName.new("File \"#{prefix}/#{name}\" has a too long base path (should be 155 or less)")
+ end
+ end
+
+ return name, prefix
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/package_task.rb b/jni/ruby/lib/rubygems/package_task.rb
new file mode 100644
index 0000000..09384cc
--- /dev/null
+++ b/jni/ruby/lib/rubygems/package_task.rb
@@ -0,0 +1,128 @@
+# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+require 'rubygems'
+require 'rubygems/package'
+begin
+ gem 'rake'
+rescue Gem::LoadError
+end
+
+require 'rake/packagetask'
+
+##
+# Create a package based upon a Gem::Specification. Gem packages, as well as
+# zip files and tar/gzipped packages can be produced by this task.
+#
+# In addition to the Rake targets generated by Rake::PackageTask, a
+# Gem::PackageTask will also generate the following tasks:
+#
+# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.gem"</b>]
+# Create a RubyGems package with the given name and version.
+#
+# Example using a Gem::Specification:
+#
+# require 'rubygems'
+# require 'rubygems/package_task'
+#
+# 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::PackageTask.new(spec) do |pkg|
+# pkg.need_zip = true
+# pkg.need_tar = true
+# end
+
+class Gem::PackageTask < Rake::PackageTask
+
+ ##
+ # Ruby Gem::Specification containing the metadata for this package. The
+ # name, version and package_files are automatically determined from the
+ # gemspec and don't need to be explicitly provided.
+
+ attr_accessor :gem_spec
+
+ ##
+ # Create a Gem Package task library. Automatically define the gem if a
+ # block is given. If no block is supplied, then #define needs to be called
+ # to define the task.
+
+ def initialize(gem_spec)
+ init gem_spec
+ yield self if block_given?
+ define if block_given?
+ end
+
+ ##
+ # Initialization tasks without the "yield self" or define operations.
+
+ def init(gem)
+ super gem.full_name, :noversion
+ @gem_spec = gem
+ @package_files += gem_spec.files if gem_spec.files
+ end
+
+ ##
+ # Create the Rake tasks and actions specified by this Gem::PackageTask.
+ # (+define+ is automatically called if a block is given to +new+).
+
+ def define
+ super
+
+ gem_file = File.basename gem_spec.cache_file
+ gem_path = File.join package_dir, gem_file
+ gem_dir = File.join package_dir, gem_spec.full_name
+
+ task :package => [:gem]
+
+ directory package_dir
+ directory gem_dir
+
+ desc "Build the gem file #{gem_file}"
+ task :gem => [gem_path]
+
+ trace = Rake.application.options.trace
+ Gem.configuration.verbose = trace
+
+ file gem_path => [package_dir, gem_dir] + @gem_spec.files do
+ chdir(gem_dir) do
+ when_writing "Creating #{gem_spec.file_name}" do
+ Gem::Package.build gem_spec
+
+ verbose trace do
+ mv gem_file, '..'
+ end
+ end
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/path_support.rb b/jni/ruby/lib/rubygems/path_support.rb
new file mode 100644
index 0000000..2af303e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/path_support.rb
@@ -0,0 +1,87 @@
+##
+#
+# Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings
+# to the rest of RubyGems.
+#
+class Gem::PathSupport
+ ##
+ # The default system path for managing Gems.
+ attr_reader :home
+
+ ##
+ # Array of paths to search for Gems.
+ attr_reader :path
+
+ ##
+ # Directory with spec cache
+ attr_reader :spec_cache_dir # :nodoc:
+
+ ##
+ #
+ # Constructor. Takes a single argument which is to be treated like a
+ # hashtable, or defaults to ENV, the system environment.
+ #
+ def initialize(env=ENV)
+ @env = env
+
+ # note 'env' vs 'ENV'...
+ @home = env["GEM_HOME"] || ENV["GEM_HOME"] || Gem.default_dir
+
+ if File::ALT_SEPARATOR then
+ @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
+ end
+
+ self.path = env["GEM_PATH"] || ENV["GEM_PATH"]
+
+ @spec_cache_dir =
+ env["GEM_SPEC_CACHE"] || ENV["GEM_SPEC_CACHE"] ||
+ Gem.default_spec_cache_dir
+
+ @spec_cache_dir = @spec_cache_dir.dup.untaint
+ end
+
+ private
+
+ ##
+ # Set the Gem home directory (as reported by Gem.dir).
+
+ def home=(home)
+ @home = home.to_s
+ end
+
+ ##
+ # Set the Gem search path (as reported by Gem.path).
+
+ def path=(gpaths)
+ # FIX: it should be [home, *path], not [*path, home]
+
+ gem_path = []
+
+ # FIX: I can't tell wtf this is doing.
+ gpaths ||= (ENV['GEM_PATH'] || "").empty? ? nil : ENV["GEM_PATH"]
+
+ if gpaths
+ if gpaths.kind_of?(Array)
+ gem_path = gpaths.dup
+ else
+ gem_path = gpaths.split(Gem.path_separator)
+ end
+
+ if File::ALT_SEPARATOR then
+ gem_path.map! do |this_path|
+ this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR
+ end
+ end
+
+ gem_path << @home
+ else
+ gem_path = Gem.default_path + [@home]
+
+ if defined?(APPLE_GEM_HOME)
+ gem_path << APPLE_GEM_HOME
+ end
+ end
+
+ @path = gem_path.uniq
+ end
+end
diff --git a/jni/ruby/lib/rubygems/platform.rb b/jni/ruby/lib/rubygems/platform.rb
new file mode 100644
index 0000000..fa56141
--- /dev/null
+++ b/jni/ruby/lib/rubygems/platform.rb
@@ -0,0 +1,204 @@
+require "rubygems/deprecate"
+
+##
+# Available list of platforms for targeting Gem installations.
+#
+# See `gem help platform` for information on platform matching.
+
+class Gem::Platform
+
+ @local = nil
+
+ attr_accessor :cpu
+
+ attr_accessor :os
+
+ attr_accessor :version
+
+ def self.local
+ arch = RbConfig::CONFIG['arch']
+ arch = "#{arch}_60" if arch =~ /mswin(?:32|64)$/
+ @local ||= new(arch)
+ end
+
+ def self.match(platform)
+ Gem.platforms.any? do |local_platform|
+ platform.nil? or
+ local_platform == platform or
+ (local_platform != Gem::Platform::RUBY and local_platform =~ platform)
+ end
+ end
+
+ def self.installable?(spec)
+ if spec.respond_to? :installable_platform?
+ spec.installable_platform?
+ else
+ match spec.platform
+ end
+ end
+
+ def self.new(arch) # :nodoc:
+ case arch
+ when Gem::Platform::CURRENT then
+ Gem::Platform.local
+ when Gem::Platform::RUBY, nil, '' then
+ Gem::Platform::RUBY
+ else
+ super
+ end
+ end
+
+ def initialize(arch)
+ case arch
+ when Array then
+ @cpu, @os, @version = arch
+ when String then
+ arch = arch.split '-'
+
+ if arch.length > 2 and arch.last !~ /\d/ then # reassemble x86-linux-gnu
+ extra = arch.pop
+ arch.last << "-#{extra}"
+ end
+
+ cpu = arch.shift
+
+ @cpu = case cpu
+ when /i\d86/ then 'x86'
+ else cpu
+ end
+
+ if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
+ @os, @version = arch
+ return
+ end
+
+ os, = arch
+ @cpu, os = nil, cpu if os.nil? # legacy jruby
+
+ @os, @version = case os
+ when /aix(\d+)?/ then [ 'aix', $1 ]
+ when /cygwin/ then [ 'cygwin', nil ]
+ when /darwin(\d+)?/ then [ 'darwin', $1 ]
+ when /^macruby$/ then [ 'macruby', nil ]
+ when /freebsd(\d+)?/ then [ 'freebsd', $1 ]
+ when /hpux(\d+)?/ then [ 'hpux', $1 ]
+ when /^java$/, /^jruby$/ then [ 'java', nil ]
+ when /^java([\d.]*)/ then [ 'java', $1 ]
+ when /^dalvik(\d+)?$/ then [ 'dalvik', $1 ]
+ when /^dotnet$/ then [ 'dotnet', nil ]
+ when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
+ when /linux/ then [ 'linux', $1 ]
+ when /mingw32/ then [ 'mingw32', nil ]
+ when /(mswin\d+)(\_(\d+))?/ then
+ os, version = $1, $3
+ @cpu = 'x86' if @cpu.nil? and os =~ /32$/
+ [os, version]
+ when /netbsdelf/ then [ 'netbsdelf', nil ]
+ when /openbsd(\d+\.\d+)?/ then [ 'openbsd', $1 ]
+ when /solaris(\d+\.\d+)?/ then [ 'solaris', $1 ]
+ # test
+ when /^(\w+_platform)(\d+)?/ then [ $1, $2 ]
+ else [ 'unknown', nil ]
+ end
+ when Gem::Platform then
+ @cpu = arch.cpu
+ @os = arch.os
+ @version = arch.version
+ else
+ raise ArgumentError, "invalid argument #{arch.inspect}"
+ end
+ end
+
+ def inspect
+ "#<%s:0x%x @cpu=%p, @os=%p, @version=%p>" % [self.class, object_id, *to_a]
+ end
+
+ def to_a
+ [@cpu, @os, @version]
+ end
+
+ def to_s
+ to_a.compact.join '-'
+ end
+
+ ##
+ # Is +other+ equal to this platform? Two platforms are equal if they have
+ # the same CPU, OS and version.
+
+ def ==(other)
+ self.class === other and to_a == other.to_a
+ end
+
+ alias :eql? :==
+
+ def hash # :nodoc:
+ to_a.hash
+ end
+
+ ##
+ # Does +other+ match this platform? Two platforms match if they have the
+ # same CPU, or either has a CPU of 'universal', they have the same OS, and
+ # they have the same version, or either has no version.
+ #
+ # Additionally, the platform will match if the local CPU is 'arm' and the
+ # other CPU starts with "arm" (for generic ARM family support).
+
+ def ===(other)
+ return nil unless Gem::Platform === other
+
+ # cpu
+ (@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu or
+ (@cpu == 'arm' and other.cpu =~ /\Aarm/)) and
+
+ # os
+ @os == other.os and
+
+ # version
+ (@version.nil? or other.version.nil? or @version == other.version)
+ end
+
+ ##
+ # Does +other+ match this platform? If +other+ is a String it will be
+ # converted to a Gem::Platform first. See #=== for matching rules.
+
+ def =~(other)
+ case other
+ when Gem::Platform then # nop
+ when String then
+ # This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007
+ other = case other
+ when /^i686-darwin(\d)/ then ['x86', 'darwin', $1 ]
+ when /^i\d86-linux/ then ['x86', 'linux', nil ]
+ when 'java', 'jruby' then [nil, 'java', nil ]
+ when /^dalvik(\d+)?$/ then [nil, 'dalvik', $1 ]
+ when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ]
+ when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $2 ]
+ when /mswin64(\_(\d+))?/ then ['x64', 'mswin64', $2 ]
+ when 'powerpc-darwin' then ['powerpc', 'darwin', nil ]
+ when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ]
+ when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ]
+ when /universal-darwin(\d)/ then ['universal', 'darwin', $1 ]
+ else other
+ end
+
+ other = Gem::Platform.new other
+ else
+ return nil
+ end
+
+ self === other
+ end
+
+ ##
+ # A pure-Ruby gem that may use Gem::Specification#extensions to build
+ # binary files.
+
+ RUBY = 'ruby'
+
+ ##
+ # A platform-specific gem that is built for the packaging Ruby's platform.
+ # This will be replaced with Gem::Platform::local.
+
+ CURRENT = 'current'
+end
+
diff --git a/jni/ruby/lib/rubygems/psych_additions.rb b/jni/ruby/lib/rubygems/psych_additions.rb
new file mode 100644
index 0000000..0e4ebbd
--- /dev/null
+++ b/jni/ruby/lib/rubygems/psych_additions.rb
@@ -0,0 +1,9 @@
+# This exists just to satify bugs in marshal'd gemspecs that
+# contain a reference to YAML::PrivateType. We prune these out
+# in Specification._load, but if we don't have the constant, Marshal
+# blows up.
+
+module Psych # :nodoc:
+ class PrivateType # :nodoc:
+ end
+end
diff --git a/jni/ruby/lib/rubygems/psych_tree.rb b/jni/ruby/lib/rubygems/psych_tree.rb
new file mode 100644
index 0000000..e3f1d1a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/psych_tree.rb
@@ -0,0 +1,31 @@
+module Gem
+ if defined? ::Psych::Visitors
+ class NoAliasYAMLTree < Psych::Visitors::YAMLTree
+ def self.create
+ new({})
+ end unless respond_to? :create
+
+ def visit_String(str)
+ return super unless str == '=' # or whatever you want
+
+ quote = Psych::Nodes::Scalar::SINGLE_QUOTED
+ @emitter.scalar str, nil, nil, false, true, quote
+ end
+
+ # Noop this out so there are no anchors
+ def register(target, obj)
+ end
+
+ # This is ported over from the yaml_tree in 1.9.3
+ def format_time time
+ if time.utc?
+ time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
+ else
+ time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
+ end
+ end
+
+ private :format_time
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/rdoc.rb b/jni/ruby/lib/rubygems/rdoc.rb
new file mode 100644
index 0000000..180b95f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/rdoc.rb
@@ -0,0 +1,335 @@
+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 ambiguous 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
+ extend 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
+ start = Time.now
+ 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
+
+ return unless generate_rdoc or generate_ri
+
+ duration = (Time.now - start).to_i
+ names = specs.map(&:name).join ', '
+
+ say "Done installing documentation for #{names} after #{duration} seconds"
+ end
+
+ ##
+ # Loads the RDoc generator
+
+ def self.load_rdoc
+ return if @rdoc_version
+
+ 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
+
+ ##
+ # 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 = true, 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+
+ #--
+ # TODO move to RDoc::Options
+
+ 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
+ generator_name = generator
+
+ options = options.dup
+ options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
+ options.setup_generator generator
+ options.op_dir = destination
+ options.finish
+
+ generator = options.generator.new @rdoc.store, options
+
+ @rdoc.options = options
+ @rdoc.generator = generator
+
+ say "Installing #{generator_name} 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
+
+ options = nil
+
+ if Gem::Requirement.new('< 3').satisfied_by? self.class.rdoc_version then
+ generate_legacy
+ return
+ end
+
+ ::RDoc::TopLevel.reset # TODO ::RDoc::RDoc.reset
+ ::RDoc::Parser::C.reset
+
+ args = @spec.rdoc_options
+ args.concat @spec.source_paths
+ args.concat @spec.extra_rdoc_files
+
+ 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
+
+ Dir.chdir @spec.full_gem_path do
+ options = ::RDoc::Options.new
+ options.default_title = "#{@spec.full_name} Documentation"
+ options.parse args
+ end
+
+ options.quiet = !Gem.configuration.really_verbose
+
+ @rdoc = new_rdoc
+ @rdoc.options = options
+
+ say "Parsing documentation for #{@spec.full_name}"
+
+ 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
+
+ ##
+ # 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
+ verbose { "rdoc #{args.join ' '}" }
+
+ 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)"
+ end
+ end
+ end
+
+ ##
+ # #new_rdoc creates a new RDoc instance. This method is provided only to
+ # make testing easier.
+
+ def new_rdoc # :nodoc:
+ ::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))
+
diff --git a/jni/ruby/lib/rubygems/remote_fetcher.rb b/jni/ruby/lib/rubygems/remote_fetcher.rb
new file mode 100644
index 0000000..b1f6dd1
--- /dev/null
+++ b/jni/ruby/lib/rubygems/remote_fetcher.rb
@@ -0,0 +1,404 @@
+require 'rubygems'
+require 'rubygems/request'
+require 'rubygems/uri_formatter'
+require 'rubygems/user_interaction'
+require 'rubygems/request/connection_pools'
+require 'resolv'
+
+##
+# RemoteFetcher handles the details of fetching gems and gem information from
+# a remote source.
+
+class Gem::RemoteFetcher
+
+ include Gem::UserInteraction
+
+ ##
+ # A FetchError exception wraps up the various possible IO and HTTP failures
+ # that could happen while downloading from the internet.
+
+ class FetchError < Gem::Exception
+
+ ##
+ # The URI which was being accessed when the exception happened.
+
+ attr_accessor :uri
+
+ def initialize(message, uri)
+ super message
+ @uri = uri
+ end
+
+ def to_s # :nodoc:
+ "#{super} (#{uri})"
+ 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
+
+ ##
+ # Cached RemoteFetcher instance.
+
+ def self.fetcher
+ @fetcher ||= self.new Gem.configuration[:http_proxy]
+ end
+
+ ##
+ # Initialize a remote fetcher using the source URI and possible proxy
+ # information.
+ #
+ # +proxy+
+ # * [String]: explicit specification of proxy; overrides any environment
+ # variable setting
+ # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
+ # HTTP_PROXY_PASS)
+ # * <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, dns=Resolv::DNS.new)
+ require 'net/http'
+ require 'stringio'
+ require 'time'
+ require 'uri'
+
+ Socket.do_not_reverse_lookup = true
+
+ @proxy = proxy
+ @pools = {}
+ @pool_lock = Mutex.new
+ @cert_files = Gem::Request.get_cert_files
+
+ @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
+ target = res.target.to_s.strip
+
+ if /\.#{Regexp.quote(host)}\z/ =~ target
+ return URI.parse "#{uri.scheme}://#{target}#{uri.path}"
+ end
+
+ uri
+ end
+ end
+
+ ##
+ # Given a name and requirement, downloads this gem into cache and returns the
+ # filename. Returns nil if the gem cannot be located.
+ #--
+ # Should probably be integrated with #download below, but that will be a
+ # larger, more emcompassing effort. -erikh
+
+ def download_to_cache dependency
+ found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
+
+ return if found.empty?
+
+ spec, source = found.max_by { |(s,_)| s.version }
+
+ download spec, source.uri.to_s
+ end
+
+ ##
+ # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is
+ # already there. If the source_uri is local the gem cache dir copy is
+ # always replaced.
+
+ def download(spec, source_uri, install_dir = Gem.dir)
+ cache_dir =
+ if Dir.pwd == install_dir then # see fetch_command
+ install_dir
+ elsif File.writable? install_dir then
+ File.join install_dir, "cache"
+ else
+ File.join Gem.user_dir, "cache"
+ end
+
+ gem_file_name = File.basename spec.cache_file
+ local_gem_path = File.join cache_dir, gem_file_name
+
+ FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
+
+ # Always escape URI's to deal with potential spaces and such
+ # It should also be considered that source_uri may already be
+ # a valid URI with escaped characters. e.g. "{DESede}" is encoded
+ # as "%7BDESede%7D". If this is escaped again the percentage
+ # symbols will be escaped.
+ unless source_uri.is_a?(URI::Generic)
+ begin
+ source_uri = URI.parse(source_uri)
+ rescue
+ source_uri = URI.parse(URI.const_defined?(:DEFAULT_PARSER) ?
+ URI::DEFAULT_PARSER.escape(source_uri.to_s) :
+ URI.escape(source_uri.to_s))
+ end
+ end
+
+ scheme = source_uri.scheme
+
+ # URI.parse gets confused by MS Windows paths with forward slashes.
+ 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
+ when 'http', 'https', 's3' then
+ unless File.exist? local_gem_path then
+ begin
+ verbose "Downloading gem #{gem_file_name}"
+
+ remote_gem_path = source_uri + "gems/#{gem_file_name}"
+
+ self.cache_update_path remote_gem_path, local_gem_path
+ rescue Gem::RemoteFetcher::FetchError
+ raise if spec.original_platform == spec.platform
+
+ alternate_name = "#{spec.original_name}.gem"
+
+ verbose "Failed, downloading gem #{alternate_name}"
+
+ remote_gem_path = source_uri + "gems/#{alternate_name}"
+
+ self.cache_update_path remote_gem_path, local_gem_path
+ end
+ end
+ when 'file' then
+ begin
+ path = source_uri.path
+ path = File.dirname(path) if File.extname(path) == '.gem'
+
+ remote_gem_path = correct_for_windows_path(File.join(path, 'gems', gem_file_name))
+
+ FileUtils.cp(remote_gem_path, local_gem_path)
+ rescue Errno::EACCES
+ local_gem_path = source_uri.to_s
+ end
+
+ verbose "Using local gem #{local_gem_path}"
+ when nil then # TODO test for local overriding cache
+ source_path = if Gem.win_platform? && source_uri.scheme &&
+ !source_uri.path.include?(':') then
+ "#{source_uri.scheme}:#{source_uri.path}"
+ else
+ source_uri.path
+ end
+
+ source_path = Gem::UriFormatter.new(source_path).unescape
+
+ begin
+ FileUtils.cp source_path, local_gem_path unless
+ File.identical?(source_path, local_gem_path)
+ rescue Errno::EACCES
+ local_gem_path = source_uri.to_s
+ end
+
+ verbose "Using local gem #{local_gem_path}"
+ else
+ raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}"
+ end
+
+ local_gem_path
+ end
+
+ ##
+ # File Fetcher. Dispatched by +fetch_path+. Use it instead.
+
+ def fetch_file uri, *_
+ Gem.read_binary correct_for_windows_path uri.path
+ end
+
+ ##
+ # HTTP Fetcher. Dispatched by +fetch_path+. Use it instead.
+
+ def fetch_http uri, last_modified = nil, head = false, depth = 0
+ fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
+ response = request uri, fetch_type, last_modified
+
+ case response
+ when Net::HTTPOK, Net::HTTPNotModified then
+ response.uri = uri if response.respond_to? :uri
+ head ? response : response.body
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
+ Net::HTTPTemporaryRedirect then
+ raise FetchError.new('too many redirects', uri) if depth > 10
+
+ location = URI.parse response['Location']
+
+ if https?(uri) && !https?(location)
+ raise FetchError.new("redirecting to non-https resource: #{location}", uri)
+ end
+
+ fetch_http(location, last_modified, head, depth + 1)
+ else
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
+ end
+ end
+
+ alias :fetch_https :fetch_http
+
+ ##
+ # Downloads +uri+ and returns it as a String.
+
+ def fetch_path(uri, mtime = nil, head = false)
+ uri = URI.parse uri unless URI::Generic === uri
+
+ raise ArgumentError, "bad uri: #{uri}" unless uri
+
+ unless uri.scheme
+ raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
+ end
+
+ data = send "fetch_#{uri.scheme}", uri, mtime, head
+
+ 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
+ rescue FetchError
+ raise
+ rescue Timeout::Error
+ raise UnknownHostError.new('timed out', uri.to_s)
+ rescue IOError, SocketError, SystemCallError => e
+ 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
+
+ def fetch_s3(uri, mtime = nil, head = false)
+ public_uri = sign_s3_url(uri)
+ fetch_https public_uri, mtime, head
+ end
+
+ ##
+ # Downloads +uri+ to +path+ if necessary. If no path is given, it just
+ # passes the data.
+
+ def cache_update_path uri, path = nil, update = true
+ mtime = path && File.stat(path).mtime rescue nil
+
+ data = fetch_path(uri, mtime)
+
+ if data == nil # indicates the server returned 304 Not Modified
+ return Gem.read_binary(path)
+ end
+
+ if update and path
+ open(path, 'wb') do |io|
+ io.flock(File::LOCK_EX)
+ io.write data
+ end
+ end
+
+ data
+ end
+
+ ##
+ # Returns the size of +uri+ in bytes.
+
+ def fetch_size(uri) # TODO: phase this out
+ response = fetch_path(uri, nil, true)
+
+ response['content-length'].to_i
+ end
+
+ def correct_for_windows_path(path)
+ if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
+ path[1..-1]
+ else
+ path
+ end
+ end
+
+ ##
+ # Performs a Net::HTTP request of type +request_class+ on +uri+ returning
+ # a Net::HTTP response object. request maintains a table of persistent
+ # connections to reduce connect overhead.
+
+ def request(uri, request_class, last_modified = nil)
+ proxy = proxy_for @proxy, uri
+ pool = pools_for(proxy).pool_for uri
+
+ request = Gem::Request.new uri, request_class, last_modified, pool
+
+ request.fetch do |req|
+ yield req if block_given?
+ end
+ end
+
+ def https?(uri)
+ uri.scheme.downcase == 'https'
+ end
+
+ def close_all
+ @pools.each_value {|pool| pool.close_all}
+ end
+
+ protected
+
+ # we have our own signing code here to avoid a dependency on the aws-sdk gem
+ # fortunately, a simple GET request isn't too complex to sign properly
+ def sign_s3_url(uri, expiration = nil)
+ require 'base64'
+ require 'openssl'
+
+ unless uri.user && uri.password
+ raise FetchError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/", uri.to_s)
+ end
+
+ expiration ||= s3_expiration
+ canonical_path = "/#{uri.host}#{uri.path}"
+ payload = "GET\n\n\n#{expiration}\n#{canonical_path}"
+ digest = OpenSSL::HMAC.digest('sha1', uri.password, payload)
+ # URI.escape is deprecated, and there isn't yet a replacement that does quite what we want
+ signature = Base64.encode64(digest).gsub("\n", '').gsub(/[\+\/=]/) { |c| BASE64_URI_TRANSLATE[c] }
+ URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{uri.user}&Expires=#{expiration}&Signature=#{signature}")
+ end
+
+ def s3_expiration
+ (Time.now + 3600).to_i # one hour from now
+ end
+
+ BASE64_URI_TRANSLATE = { '+' => '%2B', '/' => '%2F', '=' => '%3D' }.freeze
+
+ private
+
+ def proxy_for proxy, uri
+ Gem::Request.proxy_uri(proxy || Gem::Request.get_proxy_from_env(uri.scheme))
+ end
+
+ def pools_for proxy
+ @pool_lock.synchronize do
+ @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files
+ end
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/request.rb b/jni/ruby/lib/rubygems/request.rb
new file mode 100644
index 0000000..702769c
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request.rb
@@ -0,0 +1,244 @@
+require 'net/http'
+require 'thread'
+require 'time'
+require 'rubygems/user_interaction'
+
+class Gem::Request
+
+ include Gem::UserInteraction
+
+ ###
+ # Legacy. This is used in tests.
+ def self.create_with_proxy uri, request_class, last_modified, proxy # :nodoc:
+ cert_files = get_cert_files
+ proxy ||= get_proxy_from_env(uri.scheme)
+ pool = ConnectionPools.new proxy_uri(proxy), cert_files
+
+ new(uri, request_class, last_modified, pool.pool_for(uri))
+ end
+
+ def self.proxy_uri proxy # :nodoc:
+ case proxy
+ when :no_proxy then nil
+ when URI::HTTP then proxy
+ else URI.parse(proxy)
+ end
+ end
+
+ def initialize(uri, request_class, last_modified, pool)
+ @uri = uri
+ @request_class = request_class
+ @last_modified = last_modified
+ @requests = Hash.new 0
+ @user_agent = user_agent
+
+ @connection_pool = pool
+ end
+
+ def proxy_uri; @connection_pool.proxy_uri; end
+ def cert_files; @connection_pool.cert_files; end
+
+ def self.get_cert_files
+ pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
+ Dir.glob(pattern)
+ end
+
+ def self.configure_connection_for_https(connection, cert_files)
+ require 'net/https'
+ connection.use_ssl = true
+ connection.verify_mode =
+ Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
+ store = OpenSSL::X509::Store.new
+
+ if Gem.configuration.ssl_client_cert then
+ pem = File.read Gem.configuration.ssl_client_cert
+ connection.cert = OpenSSL::X509::Certificate.new pem
+ connection.key = OpenSSL::PKey::RSA.new pem
+ end
+
+ store.set_default_paths
+ cert_files.each do |ssl_cert_file|
+ store.add_file ssl_cert_file
+ end
+ if Gem.configuration.ssl_ca_cert
+ if File.directory? Gem.configuration.ssl_ca_cert
+ store.add_path Gem.configuration.ssl_ca_cert
+ else
+ store.add_file Gem.configuration.ssl_ca_cert
+ end
+ end
+ connection.cert_store = store
+ connection
+ rescue LoadError => e
+ raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
+ e.message =~ / -- openssl$/
+
+ raise Gem::Exception.new(
+ 'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
+ end
+
+ ##
+ # Creates or an HTTP connection based on +uri+, or retrieves an existing
+ # connection, using a proxy if needed.
+
+ def connection_for(uri)
+ @connection_pool.checkout
+ rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
+ Errno::EHOSTDOWN => e
+ raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
+ end
+
+ def fetch
+ request = @request_class.new @uri.request_uri
+
+ unless @uri.nil? || @uri.user.nil? || @uri.user.empty? then
+ request.basic_auth Gem::UriFormatter.new(@uri.user).unescape,
+ Gem::UriFormatter.new(@uri.password).unescape
+ end
+
+ request.add_field 'User-Agent', @user_agent
+ request.add_field 'Connection', 'keep-alive'
+ request.add_field 'Keep-Alive', '30'
+
+ if @last_modified then
+ request.add_field 'If-Modified-Since', @last_modified.httpdate
+ end
+
+ yield request if block_given?
+
+ perform_request request
+ end
+
+ ##
+ # Returns a proxy URI for the given +scheme+ if one is set in the
+ # environment variables.
+
+ def self.get_proxy_from_env scheme = 'http'
+ _scheme = scheme.downcase
+ _SCHEME = scheme.upcase
+ env_proxy = ENV["#{_scheme}_proxy"] || ENV["#{_SCHEME}_PROXY"]
+
+ no_env_proxy = env_proxy.nil? || env_proxy.empty?
+
+ return get_proxy_from_env 'http' if no_env_proxy and _scheme != 'http'
+ return :no_proxy if no_env_proxy
+
+ uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
+
+ if uri and uri.user.nil? and uri.password.nil? then
+ user = ENV["#{_scheme}_proxy_user"] || ENV["#{_SCHEME}_PROXY_USER"]
+ password = ENV["#{_scheme}_proxy_pass"] || ENV["#{_SCHEME}_PROXY_PASS"]
+
+ uri.user = Gem::UriFormatter.new(user).escape
+ uri.password = Gem::UriFormatter.new(password).escape
+ end
+
+ uri
+ end
+
+ def perform_request request # :nodoc:
+ connection = connection_for @uri
+
+ retried = false
+ bad_response = false
+
+ begin
+ @requests[connection.object_id] += 1
+
+ verbose "#{request.method} #{@uri}"
+
+ file_name = File.basename(@uri.path)
+ # perform download progress reporter only for gems
+ if request.response_body_permitted? && file_name =~ /\.gem$/
+ reporter = ui.download_reporter
+ response = connection.request(request) do |incomplete_response|
+ if Net::HTTPOK === incomplete_response
+ reporter.fetch(file_name, incomplete_response.content_length)
+ downloaded = 0
+ data = ''
+
+ incomplete_response.read_body do |segment|
+ data << segment
+ downloaded += segment.length
+ reporter.update(downloaded)
+ end
+ reporter.done
+ if incomplete_response.respond_to? :body=
+ incomplete_response.body = data
+ else
+ incomplete_response.instance_variable_set(:@body, data)
+ end
+ end
+ end
+ else
+ response = connection.request request
+ end
+
+ verbose "#{response.code} #{response.message}"
+
+ rescue Net::HTTPBadResponse
+ verbose "bad response"
+
+ reset connection
+
+ raise Gem::RemoteFetcher::FetchError.new('too many bad responses', @uri) if bad_response
+
+ bad_response = true
+ retry
+ # HACK work around EOFError bug in Net::HTTP
+ # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
+ # to install gems.
+ rescue EOFError, Timeout::Error,
+ Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
+
+ requests = @requests[connection.object_id]
+ verbose "connection reset after #{requests} requests, retrying"
+
+ raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
+
+ reset connection
+
+ retried = true
+ retry
+ end
+
+ response
+ ensure
+ @connection_pool.checkin connection
+ end
+
+ ##
+ # Resets HTTP connection +connection+.
+
+ def reset(connection)
+ @requests.delete connection.object_id
+
+ connection.finish
+ connection.start
+ end
+
+ def user_agent
+ ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
+
+ ruby_version = RUBY_VERSION
+ ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
+
+ ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
+ if RUBY_PATCHLEVEL >= 0 then
+ ua << " patchlevel #{RUBY_PATCHLEVEL}"
+ elsif defined?(RUBY_REVISION) then
+ ua << " revision #{RUBY_REVISION}"
+ end
+ ua << ")"
+
+ ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby'
+
+ ua
+ end
+
+end
+
+require 'rubygems/request/http_pool'
+require 'rubygems/request/https_pool'
+require 'rubygems/request/connection_pools'
+
diff --git a/jni/ruby/lib/rubygems/request/connection_pools.rb b/jni/ruby/lib/rubygems/request/connection_pools.rb
new file mode 100644
index 0000000..7a0a6e6
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request/connection_pools.rb
@@ -0,0 +1,83 @@
+require 'thread'
+
+class Gem::Request::ConnectionPools # :nodoc:
+
+ @client = Net::HTTP
+
+ class << self
+ attr_accessor :client
+ end
+
+ def initialize proxy_uri, cert_files
+ @proxy_uri = proxy_uri
+ @cert_files = cert_files
+ @pools = {}
+ @pool_mutex = Mutex.new
+ end
+
+ def pool_for uri
+ http_args = net_http_args(uri, @proxy_uri)
+ key = http_args + [https?(uri)]
+ @pool_mutex.synchronize do
+ @pools[key] ||=
+ if https? uri then
+ Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri)
+ else
+ Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri)
+ end
+ end
+ end
+
+ def close_all
+ @pools.each_value {|pool| pool.close_all}
+ end
+
+ private
+
+ ##
+ # 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
+
+ def https? uri
+ uri.scheme.downcase == 'https'
+ end
+
+ def no_proxy? host, env_no_proxy
+ host = host.downcase
+
+ env_no_proxy.any? do |pattern|
+ pattern = pattern.downcase
+
+ host[-pattern.length, pattern.length] == pattern or
+ (pattern.start_with? '.' and pattern[1..-1] == host)
+ end
+ end
+
+ def net_http_args uri, proxy_uri
+ net_http_args = [uri.host, uri.port]
+
+ no_proxy = get_no_proxy_from_env
+
+ if proxy_uri and not no_proxy?(uri.host, no_proxy) then
+ net_http_args + [
+ proxy_uri.host,
+ proxy_uri.port,
+ Gem::UriFormatter.new(proxy_uri.user).unescape,
+ Gem::UriFormatter.new(proxy_uri.password).unescape,
+ ]
+ elsif no_proxy? uri.host, no_proxy then
+ net_http_args + [nil, nil]
+ else
+ net_http_args
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/request/http_pool.rb b/jni/ruby/lib/rubygems/request/http_pool.rb
new file mode 100644
index 0000000..c9a1858
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request/http_pool.rb
@@ -0,0 +1,47 @@
+##
+# A connection "pool" that only manages one connection for now. Provides
+# thread safe `checkout` and `checkin` methods. The pool consists of one
+# connection that corresponds to `http_args`. This class is private, do not
+# use it.
+
+class Gem::Request::HTTPPool # :nodoc:
+ attr_reader :cert_files, :proxy_uri
+
+ def initialize http_args, cert_files, proxy_uri
+ @http_args = http_args
+ @cert_files = cert_files
+ @proxy_uri = proxy_uri
+ @queue = SizedQueue.new 1
+ @queue << nil
+ end
+
+ def checkout
+ @queue.pop || make_connection
+ end
+
+ def checkin connection
+ @queue.push connection
+ end
+
+ def close_all
+ until @queue.empty?
+ if connection = @queue.pop(true) and connection.started?
+ connection.finish
+ end
+ end
+ @queue.push(nil)
+ end
+
+ private
+
+ def make_connection
+ setup_connection Gem::Request::ConnectionPools.client.new(*@http_args)
+ end
+
+ def setup_connection connection
+ connection.start
+ connection
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/request/https_pool.rb b/jni/ruby/lib/rubygems/request/https_pool.rb
new file mode 100644
index 0000000..2e3da0a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request/https_pool.rb
@@ -0,0 +1,10 @@
+class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
+ private
+
+ def setup_connection connection
+ Gem::Request.configure_connection_for_https(connection, @cert_files)
+ super
+ end
+end
+
+
diff --git a/jni/ruby/lib/rubygems/request_set.rb b/jni/ruby/lib/rubygems/request_set.rb
new file mode 100644
index 0000000..05bfcbe
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request_set.rb
@@ -0,0 +1,413 @@
+require 'tsort'
+
+##
+# A RequestSet groups a request to activate a set of dependencies.
+#
+# nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6'
+# pg = Gem::Dependency.new 'pg', '~> 0.14'
+#
+# set = Gem::RequestSet.new nokogiri, pg
+#
+# requests = set.resolve
+#
+# p requests.map { |r| r.full_name }
+# #=> ["nokogiri-1.6.0", "mini_portile-0.5.1", "pg-0.17.0"]
+
+class Gem::RequestSet
+
+ include TSort
+
+ ##
+ # Array of gems to install even if already installed
+
+ attr_accessor :always_install
+
+ attr_reader :dependencies
+
+ attr_accessor :development
+
+ ##
+ # Errors fetching gems during resolution.
+
+ attr_reader :errors
+
+ ##
+ # Set to true if you want to install only direct development dependencies.
+
+ attr_accessor :development_shallow
+
+ ##
+ # The set of git gems imported via load_gemdeps.
+
+ attr_reader :git_set # :nodoc:
+
+ ##
+ # When true, dependency resolution is not performed, only the requested gems
+ # are installed.
+
+ attr_accessor :ignore_dependencies
+
+ attr_reader :install_dir # :nodoc:
+
+ ##
+ # If true, allow dependencies to match prerelease gems.
+
+ attr_accessor :prerelease
+
+ ##
+ # When false no remote sets are used for resolving gems.
+
+ attr_accessor :remote
+
+ attr_reader :resolver # :nodoc:
+
+ ##
+ # Sets used for resolution
+
+ attr_reader :sets # :nodoc:
+
+ ##
+ # Treat missing dependencies as silent errors
+
+ attr_accessor :soft_missing
+
+ ##
+ # The set of vendor gems imported via load_gemdeps.
+
+ attr_reader :vendor_set # :nodoc:
+
+ ##
+ # Creates a RequestSet for a list of Gem::Dependency objects, +deps+. You
+ # can then #resolve and #install the resolved list of dependencies.
+ #
+ # nokogiri = Gem::Dependency.new 'nokogiri', '~> 1.6'
+ # pg = Gem::Dependency.new 'pg', '~> 0.14'
+ #
+ # set = Gem::RequestSet.new nokogiri, pg
+
+ def initialize *deps
+ @dependencies = deps
+
+ @always_install = []
+ @conservative = false
+ @dependency_names = {}
+ @development = false
+ @development_shallow = false
+ @errors = []
+ @git_set = nil
+ @ignore_dependencies = false
+ @install_dir = Gem.dir
+ @prerelease = false
+ @remote = true
+ @requests = []
+ @sets = []
+ @soft_missing = false
+ @sorted = nil
+ @specs = nil
+ @vendor_set = nil
+
+ yield self if block_given?
+ end
+
+ ##
+ # Declare that a gem of name +name+ with +reqs+ requirements is needed.
+
+ def gem name, *reqs
+ if dep = @dependency_names[name] then
+ dep.requirement.concat reqs
+ else
+ dep = Gem::Dependency.new name, reqs
+ @dependency_names[name] = dep
+ @dependencies << dep
+ end
+ end
+
+ ##
+ # Add +deps+ Gem::Dependency objects to the set.
+
+ def import deps
+ @dependencies.concat deps
+ end
+
+ ##
+ # Installs gems for this RequestSet using the Gem::Installer +options+.
+ #
+ # If a +block+ is given an activation +request+ and +installer+ are yielded.
+ # The +installer+ will be +nil+ if a gem matching the request was already
+ # installed.
+
+ def install options, &block # :yields: request, installer
+ if dir = options[:install_dir]
+ requests = install_into dir, false, options, &block
+ return requests
+ end
+
+ cache_dir = options[:cache_dir] || Gem.dir
+ @prerelease = options[:prerelease]
+
+ requests = []
+
+ sorted_requests.each do |req|
+ if req.installed? then
+ req.spec.spec.build_extensions
+
+ if @always_install.none? { |spec| spec == req.spec.spec } then
+ yield req, nil if block_given?
+ next
+ end
+ end
+
+ path = req.download cache_dir
+
+ inst = Gem::Installer.new path, options
+
+ yield req, inst if block_given?
+
+ requests << inst.install
+ end
+
+ requests
+ ensure
+ raise if $!
+ return requests if options[:gemdeps]
+
+ specs = requests.map do |request|
+ case request
+ when Gem::Resolver::ActivationRequest then
+ request.spec.spec
+ else
+ request
+ end
+ end
+
+ require 'rubygems/dependency_installer'
+ inst = Gem::DependencyInstaller.new options
+ inst.installed_gems.replace specs
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call inst, specs
+ end unless Gem.done_installing_hooks.empty?
+ end
+
+ ##
+ # Installs from the gem dependencies files in the +:gemdeps+ option in
+ # +options+, yielding to the +block+ as in #install.
+ #
+ # If +:without_groups+ is given in the +options+, those groups in the gem
+ # dependencies file are not used. See Gem::Installer for other +options+.
+
+ def install_from_gemdeps options, &block
+ gemdeps = options[:gemdeps]
+
+ @install_dir = options[:install_dir] || Gem.dir
+ @prerelease = options[:prerelease]
+ @remote = options[:domain] != :local
+ @conservative = true if options[:conservative]
+
+ gem_deps_api = load_gemdeps gemdeps, options[:without_groups], true
+
+ resolve
+
+ if options[:explain]
+ puts "Gems to install:"
+
+ sorted_requests.each do |spec|
+ puts " #{spec.full_name}"
+ end
+
+ if Gem.configuration.really_verbose
+ @resolver.stats.display
+ end
+ else
+ installed = install options, &block
+
+ if options.fetch :lock, true then
+ lockfile =
+ Gem::RequestSet::Lockfile.new self, gemdeps, gem_deps_api.dependencies
+ lockfile.write
+ end
+
+ installed
+ end
+ end
+
+ def install_into dir, force = true, options = {}
+ gem_home, ENV['GEM_HOME'] = ENV['GEM_HOME'], dir
+
+ existing = force ? [] : specs_in(dir)
+ existing.delete_if { |s| @always_install.include? s }
+
+ dir = File.expand_path dir
+
+ installed = []
+
+ options[:development] = false
+ options[:install_dir] = dir
+ options[:only_install_dir] = true
+ @prerelease = options[:prerelease]
+
+ sorted_requests.each do |request|
+ spec = request.spec
+
+ if existing.find { |s| s.full_name == spec.full_name } then
+ yield request, nil if block_given?
+ next
+ end
+
+ spec.install options do |installer|
+ yield request, installer if block_given?
+ end
+
+ installed << request
+ end
+
+ installed
+ ensure
+ ENV['GEM_HOME'] = gem_home
+ end
+
+ ##
+ # Load a dependency management file.
+
+ def load_gemdeps path, without_groups = [], installing = false
+ @git_set = Gem::Resolver::GitSet.new
+ @vendor_set = Gem::Resolver::VendorSet.new
+
+ @git_set.root_dir = @install_dir
+
+ lockfile = Gem::RequestSet::Lockfile.new self, path
+ lockfile.parse
+
+ gf = Gem::RequestSet::GemDependencyAPI.new self, path
+ gf.installing = installing
+ gf.without_groups = without_groups if without_groups
+ gf.load
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[RequestSet:', ']' do
+ q.breakable
+
+ if @remote then
+ q.text 'remote'
+ q.breakable
+ end
+
+ if @prerelease then
+ q.text 'prerelease'
+ q.breakable
+ end
+
+ if @development_shallow then
+ q.text 'shallow development'
+ q.breakable
+ elsif @development then
+ q.text 'development'
+ q.breakable
+ end
+
+ if @soft_missing then
+ q.text 'soft missing'
+ end
+
+ q.group 2, '[dependencies:', ']' do
+ q.breakable
+ @dependencies.map do |dep|
+ q.text dep.to_s
+ q.breakable
+ end
+ end
+
+ q.breakable
+ q.text 'sets:'
+
+ q.breakable
+ q.pp @sets.map { |set| set.class }
+ end
+ end
+
+ ##
+ # Resolve the requested dependencies and return an Array of Specification
+ # objects to be activated.
+
+ def resolve set = Gem::Resolver::BestSet.new
+ @sets << set
+ @sets << @git_set
+ @sets << @vendor_set
+
+ set = Gem::Resolver.compose_sets(*@sets)
+ set.remote = @remote
+ set.prerelease = @prerelease
+
+ resolver = Gem::Resolver.new @dependencies, set
+ resolver.development = @development
+ resolver.development_shallow = @development_shallow
+ resolver.ignore_dependencies = @ignore_dependencies
+ resolver.soft_missing = @soft_missing
+
+ if @conservative
+ installed_gems = {}
+ Gem::Specification.find_all do |spec|
+ (installed_gems[spec.name] ||= []) << spec
+ end
+ resolver.skip_gems = installed_gems
+ end
+
+ @resolver = resolver
+
+ @requests = resolver.resolve
+
+ @errors = set.errors
+
+ @requests
+ 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 Gem::Resolver::CurrentSet.new
+ end
+
+ def sorted_requests
+ @sorted ||= strongly_connected_components.flatten
+ end
+
+ def specs
+ @specs ||= @requests.map { |r| r.full_spec }
+ end
+
+ def specs_in dir
+ Dir["#{dir}/specifications/*.gemspec"].map do |g|
+ Gem::Specification.load g
+ end
+ end
+
+ def tsort_each_node &block # :nodoc:
+ @requests.each(&block)
+ end
+
+ def tsort_each_child node # :nodoc:
+ node.spec.dependencies.each do |dep|
+ next if dep.type == :development and not @development
+
+ match = @requests.find { |r|
+ dep.match? r.spec.name, r.spec.version, @prerelease
+ }
+
+ unless match then
+ next if dep.type == :development and @development_shallow
+ next if @soft_missing
+ raise Gem::DependencyError,
+ "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
+ end
+
+ yield match
+ end
+ end
+
+end
+
+require 'rubygems/request_set/gem_dependency_api'
+require 'rubygems/request_set/lockfile'
diff --git a/jni/ruby/lib/rubygems/request_set/gem_dependency_api.rb b/jni/ruby/lib/rubygems/request_set/gem_dependency_api.rb
new file mode 100644
index 0000000..9aad5ab
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request_set/gem_dependency_api.rb
@@ -0,0 +1,801 @@
+##
+# A semi-compatible DSL for the Bundler Gemfile and Isolate gem dependencies
+# files.
+#
+# To work with both the Bundler Gemfile and Isolate formats this
+# implementation takes some liberties to allow compatibility with each, most
+# notably in #source.
+#
+# A basic gem dependencies file will look like the following:
+#
+# source 'https://rubygems.org'
+#
+# gem 'rails', '3.2.14a
+# gem 'devise', '~> 2.1', '>= 2.1.3'
+# gem 'cancan'
+# gem 'airbrake'
+# gem 'pg'
+#
+# RubyGems recommends saving this as gem.deps.rb over Gemfile or Isolate.
+#
+# To install the gems in this Gemfile use `gem install -g` to install it and
+# create a lockfile. The lockfile will ensure that when you make changes to
+# your gem dependencies file a minimum amount of change is made to the
+# dependencies of your gems.
+#
+# RubyGems can activate all the gems in your dependencies file at startup
+# using the RUBYGEMS_GEMDEPS environment variable or through Gem.use_gemdeps.
+# See Gem.use_gemdeps for details and warnings.
+#
+# See `gem help install` and `gem help gem_dependencies` for further details.
+
+class Gem::RequestSet::GemDependencyAPI
+
+ ENGINE_MAP = { # :nodoc:
+ :jruby => %w[jruby],
+ :jruby_18 => %w[jruby],
+ :jruby_19 => %w[jruby],
+ :maglev => %w[maglev],
+ :mri => %w[ruby],
+ :mri_18 => %w[ruby],
+ :mri_19 => %w[ruby],
+ :mri_20 => %w[ruby],
+ :mri_21 => %w[ruby],
+ :rbx => %w[rbx],
+ :ruby => %w[ruby rbx maglev],
+ :ruby_18 => %w[ruby rbx maglev],
+ :ruby_19 => %w[ruby rbx maglev],
+ :ruby_20 => %w[ruby rbx maglev],
+ :ruby_21 => %w[ruby rbx maglev],
+ }
+
+ mswin = Gem::Platform.new 'x86-mswin32'
+ mswin64 = Gem::Platform.new 'x64-mswin64'
+ x86_mingw = Gem::Platform.new 'x86-mingw32'
+ x64_mingw = Gem::Platform.new 'x64-mingw32'
+
+ PLATFORM_MAP = { # :nodoc:
+ :jruby => Gem::Platform::RUBY,
+ :jruby_18 => Gem::Platform::RUBY,
+ :jruby_19 => Gem::Platform::RUBY,
+ :maglev => Gem::Platform::RUBY,
+ :mingw => x86_mingw,
+ :mingw_18 => x86_mingw,
+ :mingw_19 => x86_mingw,
+ :mingw_20 => x86_mingw,
+ :mingw_21 => x86_mingw,
+ :mri => Gem::Platform::RUBY,
+ :mri_18 => Gem::Platform::RUBY,
+ :mri_19 => Gem::Platform::RUBY,
+ :mri_20 => Gem::Platform::RUBY,
+ :mri_21 => Gem::Platform::RUBY,
+ :mswin => mswin,
+ :mswin_18 => mswin,
+ :mswin_19 => mswin,
+ :mswin_20 => mswin,
+ :mswin_21 => mswin,
+ :mswin64 => mswin64,
+ :mswin64_19 => mswin64,
+ :mswin64_20 => mswin64,
+ :mswin64_21 => mswin64,
+ :rbx => Gem::Platform::RUBY,
+ :ruby => Gem::Platform::RUBY,
+ :ruby_18 => Gem::Platform::RUBY,
+ :ruby_19 => Gem::Platform::RUBY,
+ :ruby_20 => Gem::Platform::RUBY,
+ :ruby_21 => Gem::Platform::RUBY,
+ :x64_mingw => x64_mingw,
+ :x64_mingw_20 => x64_mingw,
+ :x64_mingw_21 => x64_mingw
+ }
+
+ gt_eq_0 = Gem::Requirement.new '>= 0'
+ tilde_gt_1_8_0 = Gem::Requirement.new '~> 1.8.0'
+ tilde_gt_1_9_0 = Gem::Requirement.new '~> 1.9.0'
+ tilde_gt_2_0_0 = Gem::Requirement.new '~> 2.0.0'
+ tilde_gt_2_1_0 = Gem::Requirement.new '~> 2.1.0'
+
+ VERSION_MAP = { # :nodoc:
+ :jruby => gt_eq_0,
+ :jruby_18 => tilde_gt_1_8_0,
+ :jruby_19 => tilde_gt_1_9_0,
+ :maglev => gt_eq_0,
+ :mingw => gt_eq_0,
+ :mingw_18 => tilde_gt_1_8_0,
+ :mingw_19 => tilde_gt_1_9_0,
+ :mingw_20 => tilde_gt_2_0_0,
+ :mingw_21 => tilde_gt_2_1_0,
+ :mri => gt_eq_0,
+ :mri_18 => tilde_gt_1_8_0,
+ :mri_19 => tilde_gt_1_9_0,
+ :mri_20 => tilde_gt_2_0_0,
+ :mri_21 => tilde_gt_2_1_0,
+ :mswin => gt_eq_0,
+ :mswin_18 => tilde_gt_1_8_0,
+ :mswin_19 => tilde_gt_1_9_0,
+ :mswin_20 => tilde_gt_2_0_0,
+ :mswin_21 => tilde_gt_2_1_0,
+ :mswin64 => gt_eq_0,
+ :mswin64_19 => tilde_gt_1_9_0,
+ :mswin64_20 => tilde_gt_2_0_0,
+ :mswin64_21 => tilde_gt_2_1_0,
+ :rbx => gt_eq_0,
+ :ruby => gt_eq_0,
+ :ruby_18 => tilde_gt_1_8_0,
+ :ruby_19 => tilde_gt_1_9_0,
+ :ruby_20 => tilde_gt_2_0_0,
+ :ruby_21 => tilde_gt_2_1_0,
+ :x64_mingw => gt_eq_0,
+ :x64_mingw_20 => tilde_gt_2_0_0,
+ :x64_mingw_21 => tilde_gt_2_1_0,
+ }
+
+ WINDOWS = { # :nodoc:
+ :mingw => :only,
+ :mingw_18 => :only,
+ :mingw_19 => :only,
+ :mingw_20 => :only,
+ :mingw_21 => :only,
+ :mri => :never,
+ :mri_18 => :never,
+ :mri_19 => :never,
+ :mri_20 => :never,
+ :mri_21 => :never,
+ :mswin => :only,
+ :mswin_18 => :only,
+ :mswin_19 => :only,
+ :mswin_20 => :only,
+ :mswin_21 => :only,
+ :mswin64 => :only,
+ :mswin64_19 => :only,
+ :mswin64_20 => :only,
+ :mswin64_21 => :only,
+ :rbx => :never,
+ :ruby => :never,
+ :ruby_18 => :never,
+ :ruby_19 => :never,
+ :ruby_20 => :never,
+ :ruby_21 => :never,
+ :x64_mingw => :only,
+ :x64_mingw_20 => :only,
+ :x64_mingw_21 => :only,
+ }
+
+ ##
+ # The gems required by #gem statements in the gem.deps.rb file
+
+ attr_reader :dependencies
+
+ ##
+ # A set of gems that are loaded via the +:git+ option to #gem
+
+ attr_reader :git_set # :nodoc:
+
+ ##
+ # A Hash containing gem names and files to require from those gems.
+
+ attr_reader :requires # :nodoc:
+
+ ##
+ # A set of gems that are loaded via the +:path+ option to #gem
+
+ attr_reader :vendor_set # :nodoc:
+
+ ##
+ # The groups of gems to exclude from installation
+
+ attr_accessor :without_groups # :nodoc:
+
+ ##
+ # Creates a new GemDependencyAPI that will add dependencies to the
+ # Gem::RequestSet +set+ based on the dependency API description in +path+.
+
+ def initialize set, path
+ @set = set
+ @path = path
+
+ @current_groups = nil
+ @current_platforms = nil
+ @current_repository = nil
+ @dependencies = {}
+ @default_sources = true
+ @git_set = @set.git_set
+ @git_sources = {}
+ @installing = false
+ @requires = Hash.new { |h, name| h[name] = [] }
+ @vendor_set = @set.vendor_set
+ @gem_sources = {}
+ @without_groups = []
+
+ git_source :github do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/"
+
+ "git://github.com/#{repo_name}.git"
+ end
+
+ git_source :bitbucket do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/"
+
+ user, = repo_name.split "/", 2
+
+ "https://#{user}@bitbucket.org/#{repo_name}.git"
+ end
+ end
+
+ ##
+ # Adds +dependencies+ to the request set if any of the +groups+ are allowed.
+ # This is used for gemspec dependencies.
+
+ def add_dependencies groups, dependencies # :nodoc:
+ return unless (groups & @without_groups).empty?
+
+ dependencies.each do |dep|
+ @set.gem dep.name, *dep.requirement
+ end
+ end
+
+ private :add_dependencies
+
+ ##
+ # Finds a gemspec with the given +name+ that lives at +path+.
+
+ def find_gemspec name, path # :nodoc:
+ glob = File.join path, "#{name}.gemspec"
+
+ spec_files = Dir[glob]
+
+ case spec_files.length
+ when 1 then
+ spec_file = spec_files.first
+
+ spec = Gem::Specification.load spec_file
+
+ return spec if spec
+
+ raise ArgumentError, "invalid gemspec #{spec_file}"
+ when 0 then
+ raise ArgumentError, "no gemspecs found at #{Dir.pwd}"
+ else
+ raise ArgumentError,
+ "found multiple gemspecs at #{Dir.pwd}, " +
+ "use the name: option to specify the one you want"
+ end
+ end
+
+ ##
+ # Changes the behavior of gem dependency file loading to installing mode.
+ # In installing mode certain restrictions are ignored such as ruby version
+ # mismatch checks.
+
+ def installing= installing # :nodoc:
+ @installing = installing
+ end
+
+ ##
+ # Loads the gem dependency file and returns self.
+
+ def load
+ instance_eval File.read(@path).untaint, @path, 1
+
+ self
+ end
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # :call-seq:
+ # gem(name)
+ # gem(name, *requirements)
+ # gem(name, *requirements, options)
+ #
+ # Specifies a gem dependency with the given +name+ and +requirements+. You
+ # may also supply +options+ following the +requirements+
+ #
+ # +options+ include:
+ #
+ # require: ::
+ # RubyGems does not provide any autorequire features so requires in a gem
+ # dependencies file are recorded but ignored.
+ #
+ # In bundler the require: option overrides the file to require during
+ # Bundler.require. By default the name of the dependency is required in
+ # Bundler. A single file or an Array of files may be given.
+ #
+ # To disable requiring any file give +false+:
+ #
+ # gem 'rake', require: false
+ #
+ # group: ::
+ # Place the dependencies in the given dependency group. A single group or
+ # an Array of groups may be given.
+ #
+ # See also #group
+ #
+ # platform: ::
+ # Only install the dependency on the given platform. A single platform or
+ # an Array of platforms may be given.
+ #
+ # See #platform for a list of platforms available.
+ #
+ # path: ::
+ # Install this dependency from an unpacked gem in the given directory.
+ #
+ # gem 'modified_gem', path: 'vendor/modified_gem'
+ #
+ # git: ::
+ # Install this dependency from a git repository:
+ #
+ # gem 'private_gem', git: git@my.company.example:private_gem.git'
+ #
+ # gist: ::
+ # Install this dependency from the gist ID:
+ #
+ # gem 'bang', gist: '1232884'
+ #
+ # github: ::
+ # Install this dependency from a github git repository:
+ #
+ # gem 'private_gem', github: 'my_company/private_gem'
+ #
+ # submodules: ::
+ # Set to +true+ to include submodules when fetching the git repository for
+ # git:, gist: and github: dependencies.
+ #
+ # ref: ::
+ # Use the given commit name or SHA for git:, gist: and github:
+ # dependencies.
+ #
+ # branch: ::
+ # Use the given branch for git:, gist: and github: dependencies.
+ #
+ # tag: ::
+ # Use the given tag for git:, gist: and github: dependencies.
+
+ def gem name, *requirements
+ options = requirements.pop if requirements.last.kind_of?(Hash)
+ options ||= {}
+
+ options[:git] = @current_repository if @current_repository
+
+ source_set = false
+
+ source_set ||= gem_path name, options
+ source_set ||= gem_git name, options
+ source_set ||= gem_git_source name, options
+
+ duplicate = @dependencies.include? name
+
+ @dependencies[name] =
+ if requirements.empty? and not source_set then
+ nil
+ elsif source_set then
+ '!'
+ else
+ requirements
+ end
+
+ return unless gem_platforms options
+
+ groups = gem_group name, options
+
+ return unless (groups & @without_groups).empty?
+
+ pin_gem_source name, :default unless source_set
+
+ gem_requires name, options
+
+ if duplicate then
+ warn <<-WARNING
+Gem dependencies file #{@path} requires #{name} more than once.
+ WARNING
+ end
+
+ @set.gem name, *requirements
+ end
+
+ ##
+ # Handles the git: option from +options+ for gem +name+.
+ #
+ # Returns +true+ if the path option was handled.
+
+ def gem_git name, options # :nodoc:
+ if gist = options.delete(:gist) then
+ options[:git] = "https://gist.github.com/#{gist}.git"
+ end
+
+ return unless repository = options.delete(:git)
+
+ pin_gem_source name, :git, repository
+
+ reference = nil
+ reference ||= options.delete :ref
+ reference ||= options.delete :branch
+ reference ||= options.delete :tag
+ reference ||= 'master'
+
+ submodules = options.delete :submodules
+
+ @git_set.add_git_gem name, repository, reference, submodules
+
+ true
+ end
+
+ private :gem_git
+
+ ##
+ # Handles a git gem option from +options+ for gem +name+ for a git source
+ # registered through git_source.
+ #
+ # Returns +true+ if the custom source option was handled.
+
+ def gem_git_source name, options # :nodoc:
+ return unless git_source = (@git_sources.keys & options.keys).last
+
+ source_callback = @git_sources[git_source]
+ source_param = options.delete git_source
+
+ git_url = source_callback.call source_param
+
+ options[:git] = git_url
+
+ gem_git name, options
+
+ true
+ end
+
+ private :gem_git_source
+
+ ##
+ # Handles the :group and :groups +options+ for the gem with the given
+ # +name+.
+
+ def gem_group name, options # :nodoc:
+ g = options.delete :group
+ all_groups = g ? Array(g) : []
+
+ groups = options.delete :groups
+ all_groups |= groups if groups
+
+ all_groups |= @current_groups if @current_groups
+
+ all_groups
+ end
+
+ private :gem_group
+
+ ##
+ # Handles the path: option from +options+ for gem +name+.
+ #
+ # Returns +true+ if the path option was handled.
+
+ def gem_path name, options # :nodoc:
+ return unless directory = options.delete(:path)
+
+ pin_gem_source name, :path, directory
+
+ @vendor_set.add_vendor_gem name, directory
+
+ true
+ end
+
+ private :gem_path
+
+ ##
+ # Handles the platforms: option from +options+. Returns true if the
+ # platform matches the current platform.
+
+ def gem_platforms options # :nodoc:
+ platform_names = Array(options.delete :platform)
+ platform_names.concat Array(options.delete :platforms)
+ platform_names.concat @current_platforms if @current_platforms
+
+ return true if platform_names.empty?
+
+ platform_names.any? do |platform_name|
+ raise ArgumentError, "unknown platform #{platform_name.inspect}" unless
+ platform = PLATFORM_MAP[platform_name]
+
+ next false unless Gem::Platform.match platform
+
+ if engines = ENGINE_MAP[platform_name] then
+ next false unless engines.include? Gem.ruby_engine
+ end
+
+ case WINDOWS[platform_name]
+ when :only then
+ next false unless Gem.win_platform?
+ when :never then
+ next false if Gem.win_platform?
+ end
+
+ VERSION_MAP[platform_name].satisfied_by? Gem.ruby_version
+ end
+ end
+
+ private :gem_platforms
+
+ ##
+ # Records the require: option from +options+ and adds those files, or the
+ # default file to the require list for +name+.
+
+ def gem_requires name, options # :nodoc:
+ if options.include? :require then
+ if requires = options.delete(:require) then
+ @requires[name].concat Array requires
+ end
+ else
+ @requires[name] << name
+ end
+ end
+
+ private :gem_requires
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Block form for specifying gems from a git +repository+.
+ #
+ # git 'https://github.com/rails/rails.git' do
+ # gem 'activesupport'
+ # gem 'activerecord'
+ # end
+
+ def git repository
+ @current_repository = repository
+
+ yield
+
+ ensure
+ @current_repository = nil
+ end
+
+ ##
+ # Defines a custom git source that uses +name+ to expand git repositories
+ # for use in gems built from git repositories. You must provide a block
+ # that accepts a git repository name for expansion.
+
+ def git_source name, &callback
+ @git_sources[name] = callback
+ end
+
+ ##
+ # Returns the basename of the file the dependencies were loaded from
+
+ def gem_deps_file # :nodoc:
+ File.basename @path
+ end
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Loads dependencies from a gemspec file.
+ #
+ # +options+ include:
+ #
+ # name: ::
+ # The name portion of the gemspec file. Defaults to searching for any
+ # gemspec file in the current directory.
+ #
+ # gemspec name: 'my_gem'
+ #
+ # path: ::
+ # The path the gemspec lives in. Defaults to the current directory:
+ #
+ # gemspec 'my_gem', path: 'gemspecs', name: 'my_gem'
+ #
+ # development_group: ::
+ # The group to add development dependencies to. By default this is
+ # :development. Only one group may be specified.
+
+ def gemspec options = {}
+ name = options.delete(:name) || '{,*}'
+ path = options.delete(:path) || '.'
+ development_group = options.delete(:development_group) || :development
+
+ spec = find_gemspec name, path
+
+ groups = gem_group spec.name, {}
+
+ self_dep = Gem::Dependency.new spec.name, spec.version
+
+ add_dependencies groups, [self_dep]
+ add_dependencies groups, spec.runtime_dependencies
+
+ @dependencies[spec.name] = '!'
+
+ spec.dependencies.each do |dep|
+ @dependencies[dep.name] = dep.requirement
+ end
+
+ groups << development_group
+
+ add_dependencies groups, spec.development_dependencies
+
+ gem_requires spec.name, options
+ end
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Block form for placing a dependency in the given +groups+.
+ #
+ # group :development do
+ # gem 'debugger'
+ # end
+ #
+ # group :development, :test do
+ # gem 'minitest'
+ # end
+ #
+ # Groups can be excluded at install time using `gem install -g --without
+ # development`. See `gem help install` and `gem help gem_dependencies` for
+ # further details.
+
+ def group *groups
+ @current_groups = groups
+
+ yield
+
+ ensure
+ @current_groups = nil
+ end
+
+ ##
+ # Pins the gem +name+ to the given +source+. Adding a gem with the same
+ # name from a different +source+ will raise an exception.
+
+ def pin_gem_source name, type = :default, source = nil
+ source_description =
+ case type
+ when :default then '(default)'
+ when :path then "path: #{source}"
+ when :git then "git: #{source}"
+ else '(unknown)'
+ end
+
+ raise ArgumentError,
+ "duplicate source #{source_description} for gem #{name}" if
+ @gem_sources.fetch(name, source) != source
+
+ @gem_sources[name] = source
+ end
+
+ private :pin_gem_source
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Block form for restricting gems to a set of platforms.
+ #
+ # The gem dependencies platform is different from Gem::Platform. A platform
+ # gem.deps.rb platform matches on the ruby engine, the ruby version and
+ # whether or not windows is allowed.
+ #
+ # :ruby, :ruby_XY ::
+ # Matches non-windows, non-jruby implementations where X and Y can be used
+ # to match releases in the 1.8, 1.9, 2.0 or 2.1 series.
+ #
+ # :mri, :mri_XY ::
+ # Matches non-windows C Ruby (Matz Ruby) or only the 1.8, 1.9, 2.0 or
+ # 2.1 series.
+ #
+ # :mingw, :mingw_XY ::
+ # Matches 32 bit C Ruby on MinGW or only the 1.8, 1.9, 2.0 or 2.1 series.
+ #
+ # :x64_mingw, :x64_mingw_XY ::
+ # Matches 64 bit C Ruby on MinGW or only the 1.8, 1.9, 2.0 or 2.1 series.
+ #
+ # :mswin, :mswin_XY ::
+ # Matches 32 bit C Ruby on Microsoft Windows or only the 1.8, 1.9, 2.0 or
+ # 2.1 series.
+ #
+ # :mswin64, :mswin64_XY ::
+ # Matches 64 bit C Ruby on Microsoft Windows or only the 1.8, 1.9, 2.0 or
+ # 2.1 series.
+ #
+ # :jruby, :jruby_XY ::
+ # Matches JRuby or JRuby in 1.8 or 1.9 mode.
+ #
+ # :maglev ::
+ # Matches Maglev
+ #
+ # :rbx ::
+ # Matches non-windows Rubinius
+ #
+ # NOTE: There is inconsistency in what environment a platform matches. You
+ # may need to read the source to know the exact details.
+
+ def platform *platforms
+ @current_platforms = platforms
+
+ yield
+
+ ensure
+ @current_platforms = nil
+ end
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Block form for restricting gems to a particular set of platforms. See
+ # #platform.
+
+ alias :platforms :platform
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Restricts this gem dependencies file to the given ruby +version+.
+ #
+ # You may also provide +engine:+ and +engine_version:+ options to restrict
+ # this gem dependencies file to a particular ruby engine and its engine
+ # version. This matching is performed by using the RUBY_ENGINE and
+ # engine_specific VERSION constants. (For JRuby, JRUBY_VERSION).
+
+ def ruby version, options = {}
+ engine = options[:engine]
+ engine_version = options[:engine_version]
+
+ raise ArgumentError,
+ 'you must specify engine_version along with the ruby engine' if
+ engine and not engine_version
+
+ return true if @installing
+
+ unless RUBY_VERSION == version then
+ message = "Your Ruby version is #{RUBY_VERSION}, " +
+ "but your #{gem_deps_file} requires #{version}"
+
+ raise Gem::RubyVersionMismatch, message
+ end
+
+ if engine and engine != Gem.ruby_engine then
+ message = "Your ruby engine is #{Gem.ruby_engine}, " +
+ "but your #{gem_deps_file} requires #{engine}"
+
+ raise Gem::RubyVersionMismatch, message
+ end
+
+ if engine_version then
+ my_engine_version = Object.const_get "#{Gem.ruby_engine.upcase}_VERSION"
+
+ if engine_version != my_engine_version then
+ message =
+ "Your ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
+ "but your #{gem_deps_file} requires #{engine} #{engine_version}"
+
+ raise Gem::RubyVersionMismatch, message
+ end
+ end
+
+ return true
+ end
+
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Sets +url+ as a source for gems for this dependency API. RubyGems uses
+ # the default configured sources if no source was given. If a source is set
+ # only that source is used.
+ #
+ # This method differs in behavior from Bundler:
+ #
+ # * The +:gemcutter+, # +:rubygems+ and +:rubyforge+ sources are not
+ # supported as they are deprecated in bundler.
+ # * The +prepend:+ option is not supported. If you wish to order sources
+ # then list them in your preferred order.
+
+ def source url
+ Gem.sources.clear if @default_sources
+
+ @default_sources = false
+
+ Gem.sources << url
+ end
+
+ # TODO: remove this typo name at RubyGems 3.0
+
+ Gem::RequestSet::GemDepedencyAPI = self # :nodoc:
+
+end
+
diff --git a/jni/ruby/lib/rubygems/request_set/lockfile.rb b/jni/ruby/lib/rubygems/request_set/lockfile.rb
new file mode 100644
index 0000000..4f2fa09
--- /dev/null
+++ b/jni/ruby/lib/rubygems/request_set/lockfile.rb
@@ -0,0 +1,650 @@
+require 'strscan'
+
+##
+# Parses a gem.deps.rb.lock file and constructs a LockSet containing the
+# dependencies found inside. If the lock file is missing no LockSet is
+# constructed.
+
+class Gem::RequestSet::Lockfile
+
+ ##
+ # Raised when a lockfile cannot be parsed
+
+ class ParseError < Gem::Exception
+
+ ##
+ # The column where the error was encountered
+
+ attr_reader :column
+
+ ##
+ # The line where the error was encountered
+
+ attr_reader :line
+
+ ##
+ # The location of the lock file
+
+ attr_reader :path
+
+ ##
+ # Raises a ParseError with the given +message+ which was encountered at a
+ # +line+ and +column+ while parsing.
+
+ def initialize message, column, line, path
+ @line = line
+ @column = column
+ @path = path
+ super "#{message} (at line #{line} column #{column})"
+ end
+
+ end
+
+ ##
+ # The platforms for this Lockfile
+
+ attr_reader :platforms
+
+ ##
+ # Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
+ # location.
+
+ def initialize request_set, gem_deps_file, dependencies = nil
+ @set = request_set
+ @dependencies = dependencies
+ @gem_deps_file = File.expand_path(gem_deps_file)
+ @gem_deps_dir = File.dirname(@gem_deps_file)
+
+ @gem_deps_file.untaint unless gem_deps_file.tainted?
+
+ @current_token = nil
+ @line = 0
+ @line_pos = 0
+ @platforms = []
+ @tokens = []
+ end
+
+ def add_DEPENDENCIES out # :nodoc:
+ out << "DEPENDENCIES"
+
+ dependencies =
+ if @dependencies then
+ @dependencies.sort_by { |name,| name }.map do |name, requirement|
+ requirement_string =
+ if '!' == requirement then
+ requirement
+ else
+ Gem::Requirement.new(requirement).for_lockfile
+ end
+
+ [name, requirement_string]
+ end
+ else
+ @requests.sort_by { |r| r.name }.map do |request|
+ spec = request.spec
+ name = request.name
+ requirement = request.request.dependency.requirement
+
+ requirement_string =
+ if [Gem::Resolver::VendorSpecification,
+ Gem::Resolver::GitSpecification].include? spec.class then
+ "!"
+ else
+ requirement.for_lockfile
+ end
+
+ [name, requirement_string]
+ end
+ end
+
+ dependencies = dependencies.map do |name, requirement_string|
+ " #{name}#{requirement_string}"
+ end
+
+ out.concat dependencies
+
+ out << nil
+ end
+
+ def add_GEM out # :nodoc:
+ return if @spec_groups.empty?
+
+ source_groups = @spec_groups.values.flatten.group_by do |request|
+ request.spec.source.uri
+ end
+
+ source_groups.sort_by { |group,| group.to_s }.map do |group, requests|
+ out << "GEM"
+ out << " remote: #{group}"
+ out << " specs:"
+
+ requests.sort_by { |request| request.name }.each do |request|
+ next if request.spec.name == 'bundler'
+ platform = "-#{request.spec.platform}" unless
+ Gem::Platform::RUBY == request.spec.platform
+
+ out << " #{request.name} (#{request.version}#{platform})"
+
+ request.full_spec.dependencies.sort.each do |dependency|
+ next if dependency.type == :development
+
+ requirement = dependency.requirement
+ out << " #{dependency.name}#{requirement.for_lockfile}"
+ end
+ end
+ out << nil
+ end
+ end
+
+ def add_GIT out
+ return unless git_requests =
+ @spec_groups.delete(Gem::Resolver::GitSpecification)
+
+ by_repository_revision = git_requests.group_by do |request|
+ source = request.spec.source
+ [source.repository, source.rev_parse]
+ end
+
+ out << "GIT"
+ by_repository_revision.each do |(repository, revision), requests|
+ out << " remote: #{repository}"
+ out << " revision: #{revision}"
+ out << " specs:"
+
+ requests.sort_by { |request| request.name }.each do |request|
+ out << " #{request.name} (#{request.version})"
+
+ dependencies = request.spec.dependencies.sort_by { |dep| dep.name }
+ dependencies.each do |dep|
+ out << " #{dep.name}#{dep.requirement.for_lockfile}"
+ end
+ end
+ end
+
+ out << nil
+ end
+
+ def relative_path_from dest, base # :nodoc:
+ dest = File.expand_path(dest)
+ base = File.expand_path(base)
+
+ if dest.index(base) == 0 then
+ offset = dest[base.size+1..-1]
+
+ return '.' unless offset
+
+ offset
+ else
+ dest
+ end
+ end
+
+ def add_PATH out # :nodoc:
+ return unless path_requests =
+ @spec_groups.delete(Gem::Resolver::VendorSpecification)
+
+ out << "PATH"
+ path_requests.each do |request|
+ directory = File.expand_path(request.spec.source.uri)
+
+ out << " remote: #{relative_path_from directory, @gem_deps_dir}"
+ out << " specs:"
+ out << " #{request.name} (#{request.version})"
+ end
+
+ out << nil
+ end
+
+ def add_PLATFORMS out # :nodoc:
+ out << "PLATFORMS"
+
+ platforms = @requests.map { |request| request.spec.platform }.uniq
+
+ platforms = platforms.sort_by { |platform| platform.to_s }
+
+ platforms.sort.each do |platform|
+ out << " #{platform}"
+ end
+
+ out << nil
+ end
+
+ ##
+ # Gets the next token for a Lockfile
+
+ def get expected_types = nil, expected_value = nil # :nodoc:
+ @current_token = @tokens.shift
+
+ type, value, column, line = @current_token
+
+ if expected_types and not Array(expected_types).include? type then
+ unget
+
+ message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
+ "expected #{expected_types.inspect}"
+
+ raise ParseError.new message, column, line, "#{@gem_deps_file}.lock"
+ end
+
+ if expected_value and expected_value != value then
+ unget
+
+ message = "unexpected token [#{type.inspect}, #{value.inspect}], " +
+ "expected [#{expected_types.inspect}, " +
+ "#{expected_value.inspect}]"
+
+ raise ParseError.new message, column, line, "#{@gem_deps_file}.lock"
+ end
+
+ @current_token
+ end
+
+ def parse # :nodoc:
+ tokenize
+
+ until @tokens.empty? do
+ type, data, column, line = get
+
+ case type
+ when :section then
+ skip :newline
+
+ case data
+ when 'DEPENDENCIES' then
+ parse_DEPENDENCIES
+ when 'GIT' then
+ parse_GIT
+ when 'GEM' then
+ parse_GEM
+ when 'PATH' then
+ parse_PATH
+ when 'PLATFORMS' then
+ parse_PLATFORMS
+ else
+ type, = get until @tokens.empty? or peek.first == :section
+ end
+ else
+ raise "BUG: unhandled token #{type} (#{data.inspect}) at line #{line} column #{column}"
+ end
+ end
+ end
+
+ def parse_DEPENDENCIES # :nodoc:
+ while not @tokens.empty? and :text == peek.first do
+ _, name, = get :text
+
+ requirements = []
+
+ case peek[0]
+ when :bang then
+ get :bang
+
+ requirements << pinned_requirement(name)
+ when :l_paren then
+ get :l_paren
+
+ loop do
+ _, op, = get :requirement
+ _, version, = get :text
+
+ requirements << "#{op} #{version}"
+
+ break unless peek[0] == :comma
+
+ get :comma
+ end
+
+ get :r_paren
+
+ if peek[0] == :bang then
+ requirements.clear
+ requirements << pinned_requirement(name)
+
+ get :bang
+ end
+ end
+
+ @set.gem name, *requirements
+
+ skip :newline
+ end
+ end
+
+ def parse_GEM # :nodoc:
+ sources = []
+
+ while [:entry, 'remote'] == peek.first(2) do
+ get :entry, 'remote'
+ _, data, = get :text
+ skip :newline
+
+ sources << Gem::Source.new(data)
+ end
+
+ sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty?
+
+ get :entry, 'specs'
+
+ skip :newline
+
+ set = Gem::Resolver::LockSet.new sources
+ last_specs = nil
+
+ while not @tokens.empty? and :text == peek.first do
+ _, name, column, = get :text
+
+ case peek[0]
+ when :newline then
+ last_specs.each do |spec|
+ spec.add_dependency Gem::Dependency.new name if column == 6
+ end
+ when :l_paren then
+ get :l_paren
+
+ type, data, = get [:text, :requirement]
+
+ if type == :text and column == 4 then
+ version, platform = data.split '-', 2
+
+ platform =
+ platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
+
+ last_specs = set.add name, version, platform
+ else
+ dependency = parse_dependency name, data
+
+ last_specs.each do |spec|
+ spec.add_dependency dependency
+ end
+ end
+
+ get :r_paren
+ else
+ raise "BUG: unknown token #{peek}"
+ end
+
+ skip :newline
+ end
+
+ @set.sets << set
+ end
+
+ def parse_GIT # :nodoc:
+ get :entry, 'remote'
+ _, repository, = get :text
+
+ skip :newline
+
+ get :entry, 'revision'
+ _, revision, = get :text
+
+ skip :newline
+
+ type, value = peek.first 2
+ if type == :entry and %w[branch ref tag].include? value then
+ get
+ get :text
+
+ skip :newline
+ end
+
+ get :entry, 'specs'
+
+ skip :newline
+
+ set = Gem::Resolver::GitSet.new
+ set.root_dir = @set.install_dir
+
+ last_spec = nil
+
+ while not @tokens.empty? and :text == peek.first do
+ _, name, column, = get :text
+
+ case peek[0]
+ when :newline then
+ last_spec.add_dependency Gem::Dependency.new name if column == 6
+ when :l_paren then
+ get :l_paren
+
+ type, data, = get [:text, :requirement]
+
+ if type == :text and column == 4 then
+ last_spec = set.add_git_spec name, data, repository, revision, true
+ else
+ dependency = parse_dependency name, data
+
+ last_spec.add_dependency dependency
+ end
+
+ get :r_paren
+ else
+ raise "BUG: unknown token #{peek}"
+ end
+
+ skip :newline
+ end
+
+ @set.sets << set
+ end
+
+ def parse_PATH # :nodoc:
+ get :entry, 'remote'
+ _, directory, = get :text
+
+ skip :newline
+
+ get :entry, 'specs'
+
+ skip :newline
+
+ set = Gem::Resolver::VendorSet.new
+ last_spec = nil
+
+ while not @tokens.empty? and :text == peek.first do
+ _, name, column, = get :text
+
+ case peek[0]
+ when :newline then
+ last_spec.add_dependency Gem::Dependency.new name if column == 6
+ when :l_paren then
+ get :l_paren
+
+ type, data, = get [:text, :requirement]
+
+ if type == :text and column == 4 then
+ last_spec = set.add_vendor_gem name, directory
+ else
+ dependency = parse_dependency name, data
+
+ last_spec.dependencies << dependency
+ end
+
+ get :r_paren
+ else
+ raise "BUG: unknown token #{peek}"
+ end
+
+ skip :newline
+ end
+
+ @set.sets << set
+ end
+
+ def parse_PLATFORMS # :nodoc:
+ while not @tokens.empty? and :text == peek.first do
+ _, name, = get :text
+
+ @platforms << name
+
+ skip :newline
+ end
+ end
+
+ ##
+ # Parses the requirements following the dependency +name+ and the +op+ for
+ # the first token of the requirements and returns a Gem::Dependency object.
+
+ def parse_dependency name, op # :nodoc:
+ return Gem::Dependency.new name, op unless peek[0] == :text
+
+ _, version, = get :text
+
+ requirements = ["#{op} #{version}"]
+
+ while peek[0] == :comma do
+ get :comma
+ _, op, = get :requirement
+ _, version, = get :text
+
+ requirements << "#{op} #{version}"
+ end
+
+ Gem::Dependency.new name, requirements
+ end
+
+ ##
+ # Peeks at the next token for Lockfile
+
+ def peek # :nodoc:
+ @tokens.first || [:EOF]
+ end
+
+ def pinned_requirement name # :nodoc:
+ spec = @set.sets.select { |set|
+ Gem::Resolver::GitSet === set or
+ Gem::Resolver::VendorSet === set
+ }.map { |set|
+ set.specs[name]
+ }.compact.first
+
+ spec.version
+ end
+
+ def skip type # :nodoc:
+ get while not @tokens.empty? and peek.first == type
+ end
+
+ ##
+ # The contents of the lock file.
+
+ def to_s
+ @set.resolve
+
+ out = []
+
+ @requests = @set.sorted_requests
+
+ @spec_groups = @requests.group_by do |request|
+ request.spec.class
+ end
+
+ add_PATH out
+
+ add_GIT out
+
+ add_GEM out
+
+ add_PLATFORMS out
+
+ add_DEPENDENCIES out
+
+ out.join "\n"
+ end
+
+ ##
+ # Calculates the column (by byte) and the line of the current token based on
+ # +byte_offset+.
+
+ def token_pos byte_offset # :nodoc:
+ [byte_offset - @line_pos, @line]
+ end
+
+ ##
+ # Converts a lock file into an Array of tokens. If the lock file is missing
+ # an empty Array is returned.
+
+ def tokenize # :nodoc:
+ @line = 0
+ @line_pos = 0
+
+ @platforms = []
+ @tokens = []
+ @current_token = nil
+
+ lock_file = "#{@gem_deps_file}.lock"
+
+ @input = File.read lock_file
+ s = StringScanner.new @input
+
+ until s.eos? do
+ pos = s.pos
+
+ pos = s.pos if leading_whitespace = s.scan(/ +/)
+
+ if s.scan(/[<|=>]{7}/) then
+ message = "your #{lock_file} contains merge conflict markers"
+ column, line = token_pos pos
+
+ raise ParseError.new message, column, line, lock_file
+ end
+
+ @tokens <<
+ case
+ when s.scan(/\r?\n/) then
+ token = [:newline, nil, *token_pos(pos)]
+ @line_pos = s.pos
+ @line += 1
+ token
+ when s.scan(/[A-Z]+/) then
+ if leading_whitespace then
+ text = s.matched
+ text += s.scan(/[^\s)]*/).to_s # in case of no match
+ [:text, text, *token_pos(pos)]
+ else
+ [:section, s.matched, *token_pos(pos)]
+ end
+ when s.scan(/([a-z]+):\s/) then
+ s.pos -= 1 # rewind for possible newline
+ [:entry, s[1], *token_pos(pos)]
+ when s.scan(/\(/) then
+ [:l_paren, nil, *token_pos(pos)]
+ when s.scan(/\)/) then
+ [:r_paren, nil, *token_pos(pos)]
+ when s.scan(/<=|>=|=|~>|<|>|!=/) then
+ [:requirement, s.matched, *token_pos(pos)]
+ when s.scan(/,/) then
+ [:comma, nil, *token_pos(pos)]
+ when s.scan(/!/) then
+ [:bang, nil, *token_pos(pos)]
+ when s.scan(/[^\s),!]*/) then
+ [:text, s.matched, *token_pos(pos)]
+ else
+ raise "BUG: can't create token for: #{s.string[s.pos..-1].inspect}"
+ end
+ end
+
+ @tokens
+ rescue Errno::ENOENT
+ @tokens
+ end
+
+ ##
+ # Ungets the last token retrieved by #get
+
+ def unget # :nodoc:
+ @tokens.unshift @current_token
+ end
+
+ ##
+ # Writes the lock file alongside the gem dependencies file
+
+ def write
+ content = to_s
+
+ open "#{@gem_deps_file}.lock", 'w' do |io|
+ io.write content
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/requirement.rb b/jni/ruby/lib/rubygems/requirement.rb
new file mode 100644
index 0000000..8b6a816
--- /dev/null
+++ b/jni/ruby/lib/rubygems/requirement.rb
@@ -0,0 +1,273 @@
+require "rubygems/version"
+require "rubygems/deprecate"
+
+# If we're being loaded after yaml was already required, then
+# load our yaml + workarounds now.
+Gem.load_yaml if defined? ::YAML
+
+##
+# A Requirement is a set of one or more version restrictions. It supports a
+# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators.
+#
+# See Gem::Version for a description on how versions and requirements work
+# together in RubyGems.
+
+class Gem::Requirement
+ 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 && v.release < r.bump }
+ }
+
+ quoted = OPS.keys.map { |k| Regexp.quote k }.join "|"
+ PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{Gem::Version::VERSION_PATTERN})\\s*" # :nodoc:
+
+ ##
+ # A regular expression that matches a requirement
+
+ PATTERN = /\A#{PATTERN_RAW}\z/
+
+ ##
+ # The default requirement matches any version
+
+ DefaultRequirement = [">=", Gem::Version.new(0)]
+
+ ##
+ # Raised when a bad requirement is encountered
+
+ class BadRequirementError < ArgumentError; end
+
+ ##
+ # Factory method to create a Gem::Requirement object. Input may be
+ # a Version, a String, or nil. Intended to simplify client code.
+ #
+ # If the input is "weird", the default version requirement is
+ # returned.
+
+ def self.create input
+ case input
+ when Gem::Requirement then
+ input
+ when Gem::Version, Array then
+ new input
+ else
+ if input.respond_to? :to_str then
+ new [input.to_str]
+ else
+ default
+ end
+ end
+ end
+
+ ##
+ # A default "version requirement" can surely _only_ be '>= 0'.
+
+ def self.default
+ new '>= 0'
+ end
+
+ ##
+ # Parse +obj+, returning an <tt>[op, version]</tt> pair. +obj+ can
+ # be a String or a Gem::Version.
+ #
+ # If +obj+ is a String, it can be either a full requirement
+ # specification, like <tt>">= 1.2"</tt>, or a simple version number,
+ # like <tt>"1.2"</tt>.
+ #
+ # parse("> 1.0") # => [">", "1.0"]
+ # parse("1.0") # => ["=", "1.0"]
+ # parse(Gem::Version.new("1.0")) # => ["=, "1.0"]
+
+ def self.parse obj
+ return ["=", obj] if Gem::Version === obj
+
+ unless PATTERN =~ obj.to_s
+ raise BadRequirementError, "Illformed requirement [#{obj.inspect}]"
+ end
+
+ if $1 == ">=" && $2 == "0"
+ DefaultRequirement
+ else
+ [$1 || "=", Gem::Version.new($2)]
+ end
+ end
+
+ ##
+ # An array of requirement pairs. The first element of the pair is
+ # the op, and the second is the Gem::Version.
+
+ attr_reader :requirements #:nodoc:
+
+ ##
+ # Constructs a requirement from +requirements+. Requirements can be
+ # Strings, Gem::Versions, or Arrays of those. +nil+ and duplicate
+ # requirements are ignored. An empty set of +requirements+ is the
+ # same as <tt>">= 0"</tt>.
+
+ def initialize *requirements
+ requirements = requirements.flatten
+ requirements.compact!
+ requirements.uniq!
+
+ if requirements.empty?
+ @requirements = [DefaultRequirement]
+ else
+ @requirements = requirements.map! { |r| self.class.parse r }
+ end
+ end
+
+ ##
+ # Concatenates the +new+ requirements onto this requirement.
+
+ def concat new
+ new = new.flatten
+ new.compact!
+ new.uniq!
+ new = new.map { |r| self.class.parse r }
+
+ @requirements.concat new
+ end
+
+ ##
+ # Formats this requirement for use in a Gem::RequestSet::Lockfile.
+
+ def for_lockfile # :nodoc:
+ return if [DefaultRequirement] == @requirements
+
+ list = requirements.sort_by { |_, version|
+ version
+ }.map { |op, version|
+ "#{op} #{version}"
+ }.uniq
+
+ " (#{list.join ', '})"
+ end
+
+ ##
+ # true if this gem has no requirements.
+
+ def none?
+ if @requirements.size == 1
+ @requirements[0] == DefaultRequirement
+ else
+ false
+ end
+ end
+
+ ##
+ # true if the requirement is for only an exact version
+
+ def exact?
+ return false unless @requirements.size == 1
+ @requirements[0][0] == "="
+ end
+
+ def as_list # :nodoc:
+ requirements.map { |op, version| "#{op} #{version}" }.sort
+ end
+
+ def hash # :nodoc:
+ requirements.hash
+ end
+
+ def marshal_dump # :nodoc:
+ fix_syck_default_key_in_requirements
+
+ [@requirements]
+ end
+
+ def marshal_load array # :nodoc:
+ @requirements = array[0]
+
+ fix_syck_default_key_in_requirements
+ end
+
+ def yaml_initialize(tag, vals) # :nodoc:
+ vals.each do |ivar, val|
+ instance_variable_set "@#{ivar}", val
+ end
+
+ Gem.load_yaml
+ fix_syck_default_key_in_requirements
+ end
+
+ def init_with coder # :nodoc:
+ yaml_initialize coder.tag, coder.map
+ end
+
+ def to_yaml_properties # :nodoc:
+ ["@requirements"]
+ end
+
+ def encode_with coder # :nodoc:
+ coder.add 'requirements', @requirements
+ end
+
+ ##
+ # A requirement is a prerelease if any of the versions inside of it
+ # are prereleases
+
+ def prerelease?
+ requirements.any? { |r| r.last.prerelease? }
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 1, 'Gem::Requirement.new(', ')' do
+ q.pp as_list
+ end
+ end
+
+ ##
+ # True if +version+ satisfies this Requirement.
+
+ 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
+ requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv }
+ end
+
+ alias :=== :satisfied_by?
+ alias :=~ :satisfied_by?
+
+ ##
+ # True if the requirement will not always match the latest version.
+
+ def specific?
+ return true if @requirements.length > 1 # GIGO, > 1, > 2 is silly
+
+ not %w[> >=].include? @requirements.first.first # grab the operator
+ end
+
+ def to_s # :nodoc:
+ as_list.join ", "
+ end
+
+ def == other # :nodoc:
+ Gem::Requirement === other and to_s == other.to_s
+ end
+
+ private
+
+ def fix_syck_default_key_in_requirements # :nodoc:
+ Gem.load_yaml
+
+ # Fixup the Syck DefaultKey bug
+ @requirements.each do |r|
+ if r[0].kind_of? Gem::SyckDefaultKey
+ r[0] = "="
+ end
+ end
+ end
+end
+
+class Gem::Version
+ # This is needed for compatibility with older yaml
+ # gemspecs.
+
+ Requirement = Gem::Requirement # :nodoc:
+end
diff --git a/jni/ruby/lib/rubygems/resolver.rb b/jni/ruby/lib/rubygems/resolver.rb
new file mode 100644
index 0000000..ef17d68
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver.rb
@@ -0,0 +1,485 @@
+require 'rubygems/dependency'
+require 'rubygems/exceptions'
+require 'rubygems/util/list'
+
+require 'uri'
+require 'net/http'
+
+##
+# 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 Gem::Resolver
+
+ ##
+ # If the DEBUG_RESOLVER environment variable is set then debugging mode is
+ # enabled for the resolver. This will display information about the state
+ # of the resolver while a set of dependencies is being resolved.
+
+ DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?
+
+ require 'pp' if DEBUG_RESOLVER
+
+ ##
+ # Contains all the conflicts encountered while doing resolution
+
+ attr_reader :conflicts
+
+ ##
+ # Set to true if all development dependencies should be considered.
+
+ attr_accessor :development
+
+ ##
+ # Set to true if immediate development dependencies should be considered.
+
+ attr_accessor :development_shallow
+
+ ##
+ # When true, no dependencies are looked up for requested gems.
+
+ attr_accessor :ignore_dependencies
+
+ ##
+ # List of dependencies that could not be found in the configured sources.
+
+ attr_reader :missing
+
+ attr_reader :stats
+
+ ##
+ # Hash of gems to skip resolution. Keyed by gem name, with arrays of
+ # gem specifications as values.
+
+ attr_accessor :skip_gems
+
+ ##
+ # When a missing dependency, don't stop. Just go on and record what was
+ # missing.
+
+ attr_accessor :soft_missing
+
+ ##
+ # Combines +sets+ into a ComposedSet that allows specification lookup in a
+ # uniform manner. If one of the +sets+ is itself a ComposedSet its sets are
+ # flattened into the result ComposedSet.
+
+ def self.compose_sets *sets
+ sets.compact!
+
+ sets = sets.map do |set|
+ case set
+ when Gem::Resolver::BestSet then
+ set
+ when Gem::Resolver::ComposedSet then
+ set.sets
+ else
+ set
+ end
+ end.flatten
+
+ case sets.length
+ when 0 then
+ raise ArgumentError, 'one set in the composition must be non-nil'
+ when 1 then
+ sets.first
+ else
+ Gem::Resolver::ComposedSet.new(*sets)
+ end
+ end
+
+ ##
+ # Creates a Resolver that queries only against the already installed gems
+ # for the +needed+ dependencies.
+
+ def self.for_current_gems needed
+ new needed, Gem::Resolver::CurrentSet.new
+ end
+
+ ##
+ # Create Resolver object which will resolve the tree starting
+ # with +needed+ Dependency objects.
+ #
+ # +set+ is an object that provides where to look for specifications to
+ # satisfy the Dependencies. This defaults to IndexSet, which will query
+ # rubygems.org.
+
+ def initialize needed, set = nil
+ @set = set || Gem::Resolver::IndexSet.new
+ @needed = needed
+
+ @conflicts = []
+ @development = false
+ @development_shallow = false
+ @ignore_dependencies = false
+ @missing = []
+ @skip_gems = {}
+ @soft_missing = false
+ @stats = Gem::Resolver::Stats.new
+ end
+
+ def explain stage, *data # :nodoc:
+ return unless DEBUG_RESOLVER
+
+ d = data.map { |x| x.pretty_inspect }.join(", ")
+ $stderr.printf "%10s %s\n", stage.to_s.upcase, d
+ end
+
+ def explain_list stage # :nodoc:
+ return unless DEBUG_RESOLVER
+
+ data = yield
+ $stderr.printf "%10s (%d entries)\n", stage.to_s.upcase, data.size
+ PP.pp data, $stderr unless data.empty?
+ end
+
+ ##
+ # Creates an ActivationRequest for the given +dep+ and the last +possible+
+ # specification.
+ #
+ # Returns the Specification and the ActivationRequest
+
+ def activation_request dep, possible # :nodoc:
+ spec = possible.pop
+
+ explain :activate, [spec.full_name, possible.size]
+ explain :possible, possible
+
+ activation_request =
+ Gem::Resolver::ActivationRequest.new spec, dep, possible
+
+ return spec, activation_request
+ end
+
+ def requests s, act, reqs=nil # :nodoc:
+ return reqs if @ignore_dependencies
+
+ s.fetch_development_dependencies if @development
+
+ s.dependencies.reverse_each do |d|
+ next if d.type == :development and not @development
+ next if d.type == :development and @development_shallow and
+ act.development?
+ next if d.type == :development and @development_shallow and
+ act.parent
+
+ reqs.add Gem::Resolver::DependencyRequest.new(d, act)
+ @stats.requirement!
+ end
+
+ @set.prefetch reqs
+
+ @stats.record_requirements reqs
+
+ reqs
+ end
+
+ ##
+ # Proceed with resolution! Returns an array of ActivationRequest objects.
+
+ def resolve
+ @conflicts = []
+
+ needed = Gem::Resolver::RequirementList.new
+
+ @needed.reverse_each do |n|
+ request = Gem::Resolver::DependencyRequest.new n, nil
+
+ needed.add request
+ @stats.requirement!
+ end
+
+ @stats.record_requirements needed
+
+ res = resolve_for needed, nil
+
+ raise Gem::DependencyResolutionError, res if
+ res.kind_of? Gem::Resolver::Conflict
+
+ res.to_a
+ end
+
+ ##
+ # Extracts the specifications that may be able to fulfill +dependency+ and
+ # returns those that match the local platform and all those that match.
+
+ def find_possible dependency # :nodoc:
+ all = @set.find_all dependency
+
+ if (skip_dep_gems = skip_gems[dependency.name]) && !skip_dep_gems.empty?
+ matching = all.select do |api_spec|
+ skip_dep_gems.any? { |s| api_spec.version == s.version }
+ end
+
+ all = matching unless matching.empty?
+ end
+
+ matching_platform = select_local_platforms all
+
+ return matching_platform, all
+ end
+
+ def handle_conflict(dep, existing) # :nodoc:
+ # 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 dependency for the activation itself.
+ # Otherwise, if there was a requester, issue it on the requester's
+ # request itself.
+ # Finally, if the existing request has no requester (toplevel) unwind to
+ # it anyway.
+
+ if existing.others_possible?
+ conflict =
+ Gem::Resolver::Conflict.new dep, existing
+ elsif dep.requester
+ depreq = dep.requester.request
+ conflict =
+ Gem::Resolver::Conflict.new depreq, existing, dep
+ elsif existing.request.requester.nil?
+ conflict =
+ Gem::Resolver::Conflict.new dep, existing
+ else
+ raise Gem::DependencyError, "Unable to figure out how to unwind conflict"
+ end
+
+ @conflicts << conflict unless @conflicts.include? conflict
+
+ return conflict
+ end
+
+ # Contains the state for attempting activation of a set of possible specs.
+ # +needed+ is a Gem::List of DependencyRequest objects that, well, need
+ # to be satisfied.
+ # +specs+ is the List of ActivationRequest that are being tested.
+ # +dep+ is the DependencyRequest that was used to generate this state.
+ # +spec+ is the Specification for this state.
+ # +possible+ is List of DependencyRequest objects that can be tried to
+ # find a complete set.
+ # +conflicts+ is a [DependencyRequest, Conflict] hit tried to
+ # activate the state.
+ #
+ State = Struct.new(:needed, :specs, :dep, :spec, :possibles, :conflicts) do
+ def summary # :nodoc:
+ nd = needed.map { |s| s.to_s }.sort if nd
+
+ if specs then
+ ss = specs.map { |s| s.full_name }.sort
+ ss.unshift ss.length
+ end
+
+ d = dep.to_s
+ d << " from #{dep.requester.full_name}" if dep.requester
+
+ ps = possibles.map { |p| p.full_name }.sort
+ ps.unshift ps.length
+
+ cs = conflicts.map do |(s, c)|
+ [s.full_name, c.conflicting_dependencies.map { |cd| cd.to_s }]
+ end
+
+ { :needed => nd, :specs => ss, :dep => d, :spec => spec.full_name,
+ :possibles => ps, :conflicts => cs }
+ end
+ 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 # :nodoc:
+ # The State objects that are used to attempt the activation tree.
+ states = []
+
+ while !needed.empty?
+ @stats.iteration!
+
+ dep = needed.remove
+ explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
+ explain_list(:next5) { needed.next5 }
+ explain_list(:specs) { Array(specs).map { |x| x.full_name }.sort }
+
+ # If there is already a spec activated for the requested name...
+ if specs && 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
+
+ conflict = handle_conflict dep, existing
+
+ return conflict unless dep.requester
+
+ explain :conflict, dep, :existing, existing.full_name
+
+ depreq = dep.requester.request
+
+ state = nil
+ until states.empty?
+ x = states.pop
+
+ i = existing.request.requester
+ explain :consider, x.spec.full_name, [depreq.name, dep.name, i ? i.name : :top]
+
+ if x.spec.name == depreq.name or
+ x.spec.name == dep.name or
+ (i && (i.name == x.spec.name))
+ explain :found, x.spec.full_name
+ state = x
+ break
+ end
+ end
+
+ return conflict unless state
+
+ @stats.backtracking!
+
+ needed, specs = resolve_for_conflict needed, specs, state
+
+ states << state unless state.possibles.empty?
+
+ next
+ end
+
+ matching, all = find_possible dep
+
+ case matching.size
+ when 0
+ resolve_for_zero dep, all
+ when 1
+ needed, specs =
+ resolve_for_single needed, specs, dep, matching
+ else
+ needed, specs =
+ resolve_for_multiple needed, specs, states, dep, matching
+ end
+ end
+
+ specs
+ end
+
+ ##
+ # Rewinds +needed+ and +specs+ to a previous state in +state+ for a conflict
+ # between +dep+ and +existing+.
+
+ def resolve_for_conflict needed, specs, state # :nodoc:
+ # We exhausted the possibles so it's definitely not going to work out,
+ # bail out.
+ raise Gem::ImpossibleDependenciesError.new state.dep, state.conflicts if
+ state.possibles.empty?
+
+ # Retry resolution with this spec and add it's dependencies
+ spec, act = activation_request state.dep, state.possibles
+
+ needed = requests spec, act, state.needed.dup
+ specs = Gem::List.prepend state.specs, act
+
+ return needed, specs
+ end
+
+ ##
+ # There are multiple +possible+ specifications for this +dep+. Updates
+ # +needed+, +specs+ and +states+ for further resolution of the +possible+
+ # choices.
+
+ def resolve_for_multiple needed, specs, states, dep, possible # :nodoc:
+ # Sort them so that we try the highest versions first.
+ possible = possible.sort_by do |s|
+ [s.source, s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ spec, act = activation_request dep, possible
+
+ # We may need to try all of +possible+, so we setup state to unwind back
+ # to current +needed+ and +specs+ so we can try another. This is code is
+ # what makes conflict resolution possible.
+ states << State.new(needed.dup, specs, dep, spec, possible, [])
+
+ @stats.record_depth states
+
+ explain :states, states.map { |s| s.dep }
+
+ needed = requests spec, act, needed
+ specs = Gem::List.prepend specs, act
+
+ return needed, specs
+ end
+
+ ##
+ # Add the spec from the +possible+ list to +specs+ and process the spec's
+ # dependencies by adding them to +needed+.
+
+ def resolve_for_single needed, specs, dep, possible # :nodoc:
+ spec, act = activation_request dep, possible
+
+ specs = Gem::List.prepend 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
+
+ return needed, specs
+ end
+
+ ##
+ # When there are no possible specifications for +dep+ our work is done.
+
+ def resolve_for_zero dep, platform_mismatch # :nodoc:
+ @missing << dep
+
+ unless @soft_missing
+ exc = Gem::UnsatisfiableDependencyError.new dep, platform_mismatch
+ exc.errors = @set.errors
+
+ raise exc
+ end
+ end
+
+ ##
+ # Returns the gems in +specs+ that match the local platform.
+
+ def select_local_platforms specs # :nodoc:
+ specs.select do |spec|
+ Gem::Platform.installable? spec
+ end
+ end
+
+end
+
+##
+# TODO remove in RubyGems 3
+
+Gem::DependencyResolver = Gem::Resolver # :nodoc:
+
+require 'rubygems/resolver/activation_request'
+require 'rubygems/resolver/conflict'
+require 'rubygems/resolver/dependency_request'
+require 'rubygems/resolver/requirement_list'
+require 'rubygems/resolver/stats'
+
+require 'rubygems/resolver/set'
+require 'rubygems/resolver/api_set'
+require 'rubygems/resolver/composed_set'
+require 'rubygems/resolver/best_set'
+require 'rubygems/resolver/current_set'
+require 'rubygems/resolver/git_set'
+require 'rubygems/resolver/index_set'
+require 'rubygems/resolver/installer_set'
+require 'rubygems/resolver/lock_set'
+require 'rubygems/resolver/vendor_set'
+
+require 'rubygems/resolver/specification'
+require 'rubygems/resolver/spec_specification'
+require 'rubygems/resolver/api_specification'
+require 'rubygems/resolver/git_specification'
+require 'rubygems/resolver/index_specification'
+require 'rubygems/resolver/installed_specification'
+require 'rubygems/resolver/local_specification'
+require 'rubygems/resolver/lock_specification'
+require 'rubygems/resolver/vendor_specification'
+
diff --git a/jni/ruby/lib/rubygems/resolver/activation_request.rb b/jni/ruby/lib/rubygems/resolver/activation_request.rb
new file mode 100644
index 0000000..56c6363
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/activation_request.rb
@@ -0,0 +1,172 @@
+##
+# Specifies a Specification object that should be activated. Also contains a
+# dependency that was used to introduce this activation.
+
+class Gem::Resolver::ActivationRequest
+
+ ##
+ # The parent request for this activation request.
+
+ attr_reader :request
+
+ ##
+ # The specification to be activated.
+
+ attr_reader :spec
+
+ ##
+ # Creates a new ActivationRequest that will activate +spec+. The parent
+ # +request+ is used to provide diagnostics in case of conflicts.
+ #
+ # +others_possible+ indicates that other specifications may also match this
+ # activation request.
+
+ def initialize spec, request, others_possible = true
+ @spec = spec
+ @request = request
+ @others_possible = others_possible
+ end
+
+ def == other # :nodoc:
+ case other
+ when Gem::Specification
+ @spec == other
+ when Gem::Resolver::ActivationRequest
+ @spec == other.spec && @request == other.request
+ else
+ false
+ end
+ end
+
+ ##
+ # Is this activation request for a development dependency?
+
+ def development?
+ @request.development?
+ end
+
+ ##
+ # Downloads a gem at +path+ and returns the file path.
+
+ def download path
+ if @spec.respond_to? :source
+ source = @spec.source
+ else
+ source = Gem.sources.first
+ end
+
+ Gem.ensure_gem_subdirectories path
+
+ source.download full_spec, path
+ end
+
+ ##
+ # The full name of the specification to be activated.
+
+ def full_name
+ @spec.full_name
+ end
+
+ ##
+ # The Gem::Specification for this activation request.
+
+ def full_spec
+ Gem::Specification === @spec ? @spec : @spec.spec
+ end
+
+ def inspect # :nodoc:
+ others =
+ case @others_possible
+ when true then # TODO remove at RubyGems 3
+ ' (others possible)'
+ when false then # TODO remove at RubyGems 3
+ nil
+ else
+ unless @others_possible.empty? then
+ others = @others_possible.map { |s| s.full_name }
+ " (others possible: #{others.join ', '})"
+ end
+ end
+
+ '#<%s for %p from %s%s>' % [
+ self.class, @spec, @request, others
+ ]
+ end
+
+ ##
+ # True if the requested gem has already been installed.
+
+ def installed?
+ case @spec
+ when Gem::Resolver::VendorSpecification then
+ true
+ else
+ this_spec = full_spec
+
+ Gem::Specification.any? do |s|
+ s == this_spec
+ end
+ end
+ end
+
+ ##
+ # The name of this activation request's specification
+
+ def name
+ @spec.name
+ end
+
+ ##
+ # Indicate if this activation is one of a set of possible
+ # requests for the same Dependency request.
+
+ def others_possible?
+ case @others_possible
+ when true, false then
+ @others_possible
+ else
+ not @others_possible.empty?
+ end
+ end
+
+ ##
+ # Return the ActivationRequest that contained the dependency
+ # that we were activated for.
+
+ def parent
+ @request.requester
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Activation request', ']' do
+ q.breakable
+ q.pp @spec
+
+ q.breakable
+ q.text ' for '
+ q.pp @request
+
+ case @others_possible
+ when false then
+ when true then
+ q.breakable
+ q.text 'others possible'
+ else
+ unless @others_possible.empty? then
+ q.breakable
+ q.text 'others '
+ q.pp @others_possible.map { |s| s.full_name }
+ end
+ end
+ end
+ end
+
+ ##
+ # The version of this activation request's specification
+
+ def version
+ @spec.version
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/api_set.rb b/jni/ruby/lib/rubygems/resolver/api_set.rb
new file mode 100644
index 0000000..17d602f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/api_set.rb
@@ -0,0 +1,125 @@
+##
+# The global rubygems pool, available via the rubygems.org API.
+# Returns instances of APISpecification.
+
+class Gem::Resolver::APISet < Gem::Resolver::Set
+
+ ##
+ # The URI for the dependency API this APISet uses.
+
+ attr_reader :dep_uri # :nodoc:
+
+ ##
+ # The Gem::Source that gems are fetched from
+
+ attr_reader :source
+
+ ##
+ # The corresponding place to fetch gems.
+
+ attr_reader :uri
+
+ ##
+ # Creates a new APISet that will retrieve gems from +uri+ using the RubyGems
+ # API URL +dep_uri+ which is described at
+ # http://guides.rubygems.org/rubygems-org-api
+
+ def initialize dep_uri = 'https://rubygems.org/api/v1/dependencies'
+ super()
+
+ dep_uri = URI dep_uri unless URI === dep_uri # for ruby 1.8
+
+ @dep_uri = dep_uri
+ @uri = dep_uri + '../..'
+
+ @data = Hash.new { |h,k| h[k] = [] }
+ @source = Gem::Source.new @uri
+
+ @to_fetch = []
+ end
+
+ ##
+ # Return an array of APISpecification objects matching
+ # DependencyRequest +req+.
+
+ def find_all req
+ res = []
+
+ return res unless @remote
+
+ if @to_fetch.include?(req.name)
+ prefetch_now
+ end
+
+ versions(req.name).each do |ver|
+ if req.dependency.match? req.name, ver[:number]
+ res << Gem::Resolver::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
+ return unless @remote
+ names = reqs.map { |r| r.dependency.name }
+ needed = names - @data.keys - @to_fetch
+
+ @to_fetch += needed
+ end
+
+ def prefetch_now # :nodoc:
+ needed, @to_fetch = @to_fetch, []
+
+ uri = @dep_uri + "?gems=#{needed.sort.join ','}"
+ str = Gem::RemoteFetcher.fetcher.fetch_path uri
+
+ loaded = []
+
+ Marshal.load(str).each do |ver|
+ name = ver[:name]
+
+ @data[name] << ver
+ loaded << name
+ end
+
+ (needed - loaded).each do |missing|
+ @data[missing] = []
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[APISet', ']' do
+ q.breakable
+ q.text "URI: #{@dep_uri}"
+
+ q.breakable
+ q.text 'gem names:'
+ q.pp @data.keys
+ end
+ end
+
+ ##
+ # Return data for all versions of the gem +name+.
+
+ def versions name # :nodoc:
+ if @data.key?(name)
+ return @data[name]
+ end
+
+ uri = @dep_uri + "?gems=#{name}"
+ str = Gem::RemoteFetcher.fetcher.fetch_path uri
+
+ Marshal.load(str).each do |ver|
+ @data[ver[:name]] << ver
+ end
+
+ @data[name]
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/api_specification.rb b/jni/ruby/lib/rubygems/resolver/api_specification.rb
new file mode 100644
index 0000000..4960e66
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/api_specification.rb
@@ -0,0 +1,85 @@
+##
+# Represents a specification retrieved via the rubygems.org API.
+#
+# This is used to avoid loading the full Specification object when all we need
+# is the name, version, and dependencies.
+
+class Gem::Resolver::APISpecification < Gem::Resolver::Specification
+
+ ##
+ # Creates an APISpecification for the given +set+ from the rubygems.org
+ # +api_data+.
+ #
+ # See http://guides.rubygems.org/rubygems-org-api/#misc_methods for the
+ # format of the +api_data+.
+
+ def initialize(set, api_data)
+ super()
+
+ @set = set
+ @name = api_data[:name]
+ @version = Gem::Version.new api_data[:number]
+ @platform = Gem::Platform.new api_data[:platform]
+ @dependencies = api_data[:dependencies].map do |name, ver|
+ Gem::Dependency.new name, ver.split(/\s*,\s*/)
+ end
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @name == other.name and
+ @version == other.version and
+ @platform == other.platform and
+ @dependencies == other.dependencies
+ end
+
+ def fetch_development_dependencies # :nodoc:
+ spec = source.fetch_spec Gem::NameTuple.new @name, @version, @platform
+
+ @dependencies = spec.dependencies
+ end
+
+ def installable_platform? # :nodoc:
+ Gem::Platform.match @platform
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[APISpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp @dependencies
+
+ q.breakable
+ q.text "set uri: #{@set.dep_uri}"
+ end
+ end
+
+ ##
+ # Fetches a Gem::Specification for this APISpecification.
+
+ def spec # :nodoc:
+ @spec ||=
+ begin
+ tuple = Gem::NameTuple.new @name, @version, @platform
+
+ source.fetch_spec tuple
+ end
+ end
+
+ def source # :nodoc:
+ @set.source
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/best_set.rb b/jni/ruby/lib/rubygems/resolver/best_set.rb
new file mode 100644
index 0000000..7e2d7e2
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/best_set.rb
@@ -0,0 +1,78 @@
+##
+# The BestSet chooses the best available method to query a remote index.
+#
+# It combines IndexSet and APISet
+
+class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
+
+ ##
+ # Creates a BestSet for the given +sources+ or Gem::sources if none are
+ # specified. +sources+ must be a Gem::SourceList.
+
+ def initialize sources = Gem.sources
+ super()
+
+ @sources = sources
+ end
+
+ ##
+ # Picks which sets to use for the configured sources.
+
+ def pick_sets # :nodoc:
+ @sources.each_source do |source|
+ @sets << source.dependency_resolver_set
+ end
+ end
+
+ def find_all req # :nodoc:
+ pick_sets if @remote and @sets.empty?
+
+ super
+ rescue Gem::RemoteFetcher::FetchError => e
+ replace_failed_api_set e
+
+ retry
+ end
+
+ def prefetch reqs # :nodoc:
+ pick_sets if @remote and @sets.empty?
+
+ super
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[BestSet', ']' do
+ q.breakable
+ q.text 'sets:'
+
+ q.breakable
+ q.pp @sets
+ end
+ end
+
+ ##
+ # Replaces a failed APISet for the URI in +error+ with an IndexSet.
+ #
+ # If no matching APISet can be found the original +error+ is raised.
+ #
+ # The calling method must retry the exception to repeat the lookup.
+
+ def replace_failed_api_set error # :nodoc:
+ uri = error.uri
+ uri = URI uri unless URI === uri
+ uri.query = nil
+
+ raise error unless api_set = @sets.find { |set|
+ Gem::Resolver::APISet === set and set.dep_uri == uri
+ }
+
+ index_set = Gem::Resolver::IndexSet.new api_set.source
+
+ @sets.map! do |set|
+ next set unless set == api_set
+ index_set
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/composed_set.rb b/jni/ruby/lib/rubygems/resolver/composed_set.rb
new file mode 100644
index 0000000..5b08f12
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/composed_set.rb
@@ -0,0 +1,66 @@
+##
+# A ComposedSet allows multiple sets to be queried like a single set.
+#
+# To create a composed set with any number of sets use:
+#
+# Gem::Resolver.compose_sets set1, set2
+#
+# This method will eliminate nesting of composed sets.
+
+class Gem::Resolver::ComposedSet < Gem::Resolver::Set
+
+ attr_reader :sets # :nodoc:
+
+ ##
+ # Creates a new ComposedSet containing +sets+. Use
+ # Gem::Resolver::compose_sets instead.
+
+ def initialize *sets
+ super()
+
+ @sets = sets
+ end
+
+ ##
+ # When +allow_prerelease+ is set to +true+ prereleases gems are allowed to
+ # match dependencies.
+
+ def prerelease= allow_prerelease
+ super
+
+ sets.each do |set|
+ set.prerelease = allow_prerelease
+ end
+ end
+
+ ##
+ # Sets the remote network access for all composed sets.
+
+ def remote= remote
+ super
+
+ @sets.each { |set| set.remote = remote }
+ end
+
+ def errors
+ @errors + @sets.map { |set| set.errors }.flatten
+ end
+
+ ##
+ # Finds all specs matching +req+ in all sets.
+
+ def find_all req
+ @sets.map do |s|
+ s.find_all req
+ end.flatten
+ end
+
+ ##
+ # Prefetches +reqs+ in all sets.
+
+ def prefetch reqs
+ @sets.each { |s| s.prefetch(reqs) }
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/conflict.rb b/jni/ruby/lib/rubygems/resolver/conflict.rb
new file mode 100644
index 0000000..902c286
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/conflict.rb
@@ -0,0 +1,160 @@
+##
+# Used internally to indicate that a dependency conflicted
+# with a spec that would be activated.
+
+class Gem::Resolver::Conflict
+
+ ##
+ # The specification that was activated prior to the conflict
+
+ attr_reader :activated
+
+ ##
+ # The dependency that is in conflict with the activated gem.
+
+ attr_reader :dependency
+
+ attr_reader :failed_dep # :nodoc:
+
+ ##
+ # Creates a new resolver conflict when +dependency+ is in conflict with an
+ # already +activated+ specification.
+
+ def initialize(dependency, activated, failed_dep=dependency)
+ @dependency = dependency
+ @activated = activated
+ @failed_dep = failed_dep
+ end
+
+ def == other # :nodoc:
+ self.class === other and
+ @dependency == other.dependency and
+ @activated == other.activated and
+ @failed_dep == other.failed_dep
+ end
+
+ ##
+ # A string explanation of the conflict.
+
+ def explain
+ "<Conflict wanted: #{@failed_dep}, had: #{activated.spec.full_name}>"
+ end
+
+ ##
+ # Return the 2 dependency objects that conflicted
+
+ def conflicting_dependencies
+ [@failed_dep.dependency, @activated.request.dependency]
+ end
+
+ ##
+ # Explanation of the conflict used by exceptions to print useful messages
+
+ def explanation
+ activated = @activated.spec.full_name
+ dependency = @failed_dep.dependency
+ requirement = dependency.requirement
+ alternates = dependency.matching_specs.map { |spec| spec.full_name }
+
+ unless alternates.empty? then
+ matching = <<-MATCHING.chomp
+
+ Gems matching %s:
+ %s
+ MATCHING
+
+ matching = matching % [
+ dependency,
+ alternates.join(', '),
+ ]
+ end
+
+ explanation = <<-EXPLANATION
+ Activated %s
+ which does not match conflicting dependency (%s)
+
+ Conflicting dependency chains:
+ %s
+
+ versus:
+ %s
+%s
+ EXPLANATION
+
+ explanation % [
+ activated, requirement,
+ request_path(@activated).reverse.join(", depends on\n "),
+ request_path(@failed_dep).reverse.join(", depends on\n "),
+ matching,
+ ]
+ end
+
+ ##
+ # Returns true if the conflicting dependency's name matches +spec+.
+
+ def for_spec?(spec)
+ @dependency.name == spec.name
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Dependency conflict: ', ']' do
+ q.breakable
+
+ q.text 'activated '
+ q.pp @activated
+
+ q.breakable
+ q.text ' dependency '
+ q.pp @dependency
+
+ q.breakable
+ if @dependency == @failed_dep then
+ q.text ' failed'
+ else
+ q.text ' failed dependency '
+ q.pp @failed_dep
+ end
+ end
+ end
+
+ ##
+ # Path of activations from the +current+ list.
+
+ def request_path current
+ path = []
+
+ while current do
+ case current
+ when Gem::Resolver::ActivationRequest then
+ path <<
+ "#{current.request.dependency}, #{current.spec.version} activated"
+
+ current = current.parent
+ when Gem::Resolver::DependencyRequest then
+ path << "#{current.dependency}"
+
+ current = current.requester
+ else
+ raise Gem::Exception, "[BUG] unknown request class #{current.class}"
+ end
+ end
+
+ path = ['user request (gem command or Gemfile)'] if path.empty?
+
+ path
+ end
+
+ ##
+ # Return the Specification that listed the dependency
+
+ def requester
+ @failed_dep.requester
+ end
+
+end
+
+##
+# TODO: Remove in RubyGems 3
+
+Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict # :nodoc:
+
diff --git a/jni/ruby/lib/rubygems/resolver/current_set.rb b/jni/ruby/lib/rubygems/resolver/current_set.rb
new file mode 100644
index 0000000..4e8d340
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/current_set.rb
@@ -0,0 +1,13 @@
+##
+# A set which represents the installed gems. Respects
+# all the normal settings that control where to look
+# for installed gems.
+
+class Gem::Resolver::CurrentSet < Gem::Resolver::Set
+
+ def find_all req
+ req.dependency.matching_specs
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/dependency_request.rb b/jni/ruby/lib/rubygems/resolver/dependency_request.rb
new file mode 100644
index 0000000..79690be
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/dependency_request.rb
@@ -0,0 +1,116 @@
+##
+# Used Internally. Wraps a Dependency object to also track which spec
+# contained the Dependency.
+
+class Gem::Resolver::DependencyRequest
+
+ ##
+ # The wrapped Gem::Dependency
+
+ attr_reader :dependency
+
+ ##
+ # The request for this dependency.
+
+ attr_reader :requester
+
+ ##
+ # Creates a new DependencyRequest for +dependency+ from +requester+.
+ # +requester may be nil if the request came from a user.
+
+ def initialize dependency, requester
+ @dependency = dependency
+ @requester = requester
+ end
+
+ def == other # :nodoc:
+ case other
+ when Gem::Dependency
+ @dependency == other
+ when Gem::Resolver::DependencyRequest
+ @dependency == other.dependency && @requester == other.requester
+ else
+ false
+ end
+ end
+
+ ##
+ # Is this dependency a development dependency?
+
+ def development?
+ @dependency.type == :development
+ end
+
+ ##
+ # Does this dependency request match +spec+?
+ #
+ # NOTE: #match? only matches prerelease versions when #dependency is a
+ # prerelease dependency.
+
+ def match? spec, allow_prerelease = false
+ @dependency.match? spec, nil, allow_prerelease
+ end
+
+ ##
+ # Does this dependency request match +spec+?
+ #
+ # NOTE: #matches_spec? matches prerelease versions. See also #match?
+
+ def matches_spec?(spec)
+ @dependency.matches_spec? spec
+ end
+
+ ##
+ # The name of the gem this dependency request is requesting.
+
+ def name
+ @dependency.name
+ end
+
+ ##
+ # Indicate that the request is for a gem explicitly requested by the user
+
+ def explicit?
+ @requester.nil?
+ end
+
+ ##
+ # Indicate that the request is for a gem requested as a dependency of
+ # another gem
+
+ def implicit?
+ !explicit?
+ end
+
+ ##
+ # Return a String indicating who caused this request to be added (only
+ # valid for implicit requests)
+
+ def request_context
+ @requester ? @requester.request : "(unknown)"
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Dependency request ', ']' do
+ q.breakable
+ q.text @dependency.to_s
+
+ q.breakable
+ q.text ' requested by '
+ q.pp @requester
+ end
+ end
+
+ ##
+ # The version requirement for this dependency request
+
+ def requirement
+ @dependency.requirement
+ end
+
+ def to_s # :nodoc:
+ @dependency.to_s
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/git_set.rb b/jni/ruby/lib/rubygems/resolver/git_set.rb
new file mode 100644
index 0000000..5f1b368
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/git_set.rb
@@ -0,0 +1,122 @@
+##
+# A GitSet represents gems that are sourced from git repositories.
+#
+# This is used for gem dependency file support.
+#
+# Example:
+#
+# set = Gem::Resolver::GitSet.new
+# set.add_git_gem 'rake', 'git://example/rake.git', tag: 'rake-10.1.0'
+
+class Gem::Resolver::GitSet < Gem::Resolver::Set
+
+ ##
+ # The root directory for git gems in this set. This is usually Gem.dir, the
+ # installation directory for regular gems.
+
+ attr_accessor :root_dir
+
+ ##
+ # Contains repositories needing submodules
+
+ attr_reader :need_submodules # :nodoc:
+
+ ##
+ # A Hash containing git gem names for keys and a Hash of repository and
+ # git commit reference as values.
+
+ attr_reader :repositories # :nodoc:
+
+ ##
+ # A hash of gem names to Gem::Resolver::GitSpecifications
+
+ attr_reader :specs # :nodoc:
+
+ def initialize # :nodoc:
+ super()
+
+ @git = ENV['git'] || 'git'
+ @need_submodules = {}
+ @repositories = {}
+ @root_dir = Gem.dir
+ @specs = {}
+ end
+
+ def add_git_gem name, repository, reference, submodules # :nodoc:
+ @repositories[name] = [repository, reference]
+ @need_submodules[repository] = submodules
+ end
+
+ ##
+ # Adds and returns a GitSpecification with the given +name+ and +version+
+ # which came from a +repository+ at the given +reference+. If +submodules+
+ # is true they are checked out along with the repository.
+ #
+ # This fills in the prefetch information as enough information about the gem
+ # is present in the arguments.
+
+ def add_git_spec name, version, repository, reference, submodules # :nodoc:
+ add_git_gem name, repository, reference, submodules
+
+ source = Gem::Source::Git.new name, repository, reference
+ source.root_dir = @root_dir
+
+ spec = Gem::Specification.new do |s|
+ s.name = name
+ s.version = version
+ end
+
+ git_spec = Gem::Resolver::GitSpecification.new self, spec, source
+
+ @specs[spec.name] = git_spec
+
+ git_spec
+ end
+
+ ##
+ # Finds all git gems matching +req+
+
+ def find_all req
+ prefetch nil
+
+ specs.values.select do |spec|
+ req.match? spec
+ end
+ end
+
+ ##
+ # Prefetches specifications from the git repositories in this set.
+
+ def prefetch reqs
+ return unless @specs.empty?
+
+ @repositories.each do |name, (repository, reference)|
+ source = Gem::Source::Git.new name, repository, reference
+ source.root_dir = @root_dir
+ source.remote = @remote
+
+ source.specs.each do |spec|
+ git_spec = Gem::Resolver::GitSpecification.new self, spec, source
+
+ @specs[spec.name] = git_spec
+ end
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[GitSet', ']' do
+ next if @repositories.empty?
+ q.breakable
+
+ repos = @repositories.map do |name, (repository, reference)|
+ "#{name}: #{repository}@#{reference}"
+ end
+
+ q.seplist repos do |repo|
+ q.text repo
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/git_specification.rb b/jni/ruby/lib/rubygems/resolver/git_specification.rb
new file mode 100644
index 0000000..55e180e
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/git_specification.rb
@@ -0,0 +1,59 @@
+##
+# A GitSpecification represents a gem that is sourced from a git repository
+# and is being loaded through a gem dependencies file through the +git:+
+# option.
+
+class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @spec == other.spec and
+ @source == other.source
+ end
+
+ def add_dependency dependency # :nodoc:
+ spec.dependencies << dependency
+ end
+
+ ##
+ # Installing a git gem only involves building the extensions and generating
+ # the executables.
+
+ def install options = {}
+ require 'rubygems/installer'
+
+ installer = Gem::Installer.new '', options
+ installer.spec = spec
+
+ yield installer if block_given?
+
+ installer.run_pre_install_hooks
+ installer.build_extensions
+ installer.run_post_build_hooks
+ installer.generate_bin
+ installer.run_post_install_hooks
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[GitSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp dependencies
+
+ q.breakable
+ q.text "source:"
+ q.breakable
+ q.pp @source
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/index_set.rb b/jni/ruby/lib/rubygems/resolver/index_set.rb
new file mode 100644
index 0000000..7c56c2b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/index_set.rb
@@ -0,0 +1,80 @@
+##
+# The global rubygems pool represented via the traditional
+# source index.
+
+class Gem::Resolver::IndexSet < Gem::Resolver::Set
+
+ def initialize source = nil # :nodoc:
+ super()
+
+ @f =
+ if source then
+ sources = Gem::SourceList.from [source]
+
+ Gem::SpecFetcher.new sources
+ else
+ Gem::SpecFetcher.fetcher
+ end
+
+ @all = Hash.new { |h,k| h[k] = [] }
+
+ list, errors = @f.available_specs :complete
+
+ @errors.concat errors
+
+ 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 = []
+
+ return res unless @remote
+
+ name = req.dependency.name
+
+ @all[name].each do |uri, n|
+ if req.match? n, @prerelease then
+ res << Gem::Resolver::IndexSpecification.new(
+ self, n.name, n.version, uri, n.platform)
+ end
+ end
+
+ res
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[IndexSet', ']' do
+ q.breakable
+ q.text 'sources:'
+ q.breakable
+ q.pp @f.sources
+
+ q.breakable
+ q.text 'specs:'
+
+ q.breakable
+
+ names = @all.values.map do |tuples|
+ tuples.map do |_, tuple|
+ tuple.full_name
+ end
+ end.flatten
+
+ q.seplist names do |name|
+ q.text name
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/index_specification.rb b/jni/ruby/lib/rubygems/resolver/index_specification.rb
new file mode 100644
index 0000000..56fecb5
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/index_specification.rb
@@ -0,0 +1,69 @@
+##
+# 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 Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
+
+ ##
+ # An IndexSpecification is created from the index format described in `gem
+ # help generate_index`.
+ #
+ # The +set+ contains other specifications for this (URL) +source+.
+ #
+ # The +name+, +version+ and +platform+ are the name, version and platform of
+ # the gem.
+
+ def initialize set, name, version, source, platform
+ super()
+
+ @set = set
+ @name = name
+ @version = version
+ @source = source
+ @platform = platform.to_s
+
+ @spec = nil
+ end
+
+ ##
+ # The dependencies of the gem for this specification
+
+ def dependencies
+ spec.dependencies
+ end
+
+ def inspect # :nodoc:
+ '#<%s %s source %s>' % [self.class, full_name, @source]
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Index specification', ']' do
+ q.breakable
+ q.text full_name
+
+ unless Gem::Platform::RUBY == @platform then
+ q.breakable
+ q.text @platform.to_s
+ end
+
+ q.breakable
+ q.text 'source '
+ q.pp @source
+ end
+ end
+
+ ##
+ # Fetches a Gem::Specification for this IndexSpecification from the #source.
+
+ def spec # :nodoc:
+ @spec ||=
+ begin
+ tuple = Gem::NameTuple.new @name, @version, @platform
+
+ @source.fetch_spec tuple
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/installed_specification.rb b/jni/ruby/lib/rubygems/resolver/installed_specification.rb
new file mode 100644
index 0000000..2a2b89a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/installed_specification.rb
@@ -0,0 +1,58 @@
+##
+# An InstalledSpecification represents a gem that is already installed
+# locally.
+
+class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @spec == other.spec
+ end
+
+ ##
+ # This is a null install as this specification is already installed.
+ # +options+ are ignored.
+
+ def install options = {}
+ yield nil
+ end
+
+ ##
+ # Returns +true+ if this gem is installable for the current platform.
+
+ def installable_platform?
+ # BACKCOMPAT If the file is coming out of a specified file, then we
+ # ignore the platform. This code can be removed in RG 3.0.
+ return true if @source.kind_of? Gem::Source::SpecificFile
+
+ super
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[InstalledSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp spec.dependencies
+ end
+ end
+
+ ##
+ # The source for this specification
+
+ def source
+ @source ||= Gem::Source::Installed.new
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/installer_set.rb b/jni/ruby/lib/rubygems/resolver/installer_set.rb
new file mode 100644
index 0000000..a68ff09
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/installer_set.rb
@@ -0,0 +1,224 @@
+##
+# A set of gems for installation sourced from remote sources and local .gem
+# files
+
+class Gem::Resolver::InstallerSet < Gem::Resolver::Set
+
+ ##
+ # List of Gem::Specification objects that must always be installed.
+
+ attr_reader :always_install # :nodoc:
+
+ ##
+ # Only install gems in the always_install list
+
+ attr_accessor :ignore_dependencies # :nodoc:
+
+ ##
+ # Do not look in the installed set when finding specifications. This is
+ # used by the --install-dir option to `gem install`
+
+ attr_accessor :ignore_installed # :nodoc:
+
+ ##
+ # The remote_set looks up remote gems for installation.
+
+ attr_reader :remote_set # :nodoc:
+
+ ##
+ # Creates a new InstallerSet that will look for gems in +domain+.
+
+ def initialize domain
+ super()
+
+ @domain = domain
+ @remote = consider_remote?
+
+ @f = Gem::SpecFetcher.fetcher
+
+ @always_install = []
+ @ignore_dependencies = false
+ @ignore_installed = false
+ @local = {}
+ @remote_set = Gem::Resolver::BestSet.new
+ @specs = {}
+ end
+
+ ##
+ # Looks up the latest specification for +dependency+ and adds it to the
+ # always_install list.
+
+ def add_always_install dependency
+ request = Gem::Resolver::DependencyRequest.new dependency, nil
+
+ found = find_all request
+
+ found.delete_if { |s|
+ s.version.prerelease? and not s.local?
+ } unless dependency.prerelease?
+
+ found = found.select do |s|
+ Gem::Source::SpecificFile === s.source or
+ Gem::Platform::RUBY == s.platform or
+ Gem::Platform.local === s.platform
+ end
+
+ if found.empty? then
+ exc = Gem::UnsatisfiableDependencyError.new request
+ exc.errors = errors
+
+ raise exc
+ end
+
+ newest = found.max_by do |s|
+ [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ @always_install << newest.spec
+ end
+
+ ##
+ # Adds a local gem requested using +dep_name+ with the given +spec+ that can
+ # be loaded and installed using the +source+.
+
+ def add_local dep_name, spec, source
+ @local[dep_name] = [spec, source]
+ end
+
+ ##
+ # Should local gems should be considered?
+
+ def consider_local? # :nodoc:
+ @domain == :both or @domain == :local
+ end
+
+ ##
+ # Should remote gems should be considered?
+
+ def consider_remote? # :nodoc:
+ @domain == :both or @domain == :remote
+ end
+
+ ##
+ # Errors encountered while resolving gems
+
+ def errors
+ @errors + @remote_set.errors
+ end
+
+ ##
+ # Returns an array of IndexSpecification objects matching DependencyRequest
+ # +req+.
+
+ def find_all req
+ res = []
+
+ dep = req.dependency
+
+ return res if @ignore_dependencies and
+ @always_install.none? { |spec| dep.match? spec }
+
+ name = dep.name
+
+ dep.matching_specs.each do |gemspec|
+ next if @always_install.any? { |spec| spec.name == gemspec.name }
+
+ res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
+ end unless @ignore_installed
+
+ if consider_local? then
+ matching_local = @local.values.select do |spec, _|
+ req.match? spec
+ end.map do |spec, source|
+ Gem::Resolver::LocalSpecification.new self, spec, source
+ end
+
+ res.concat matching_local
+
+ local_source = Gem::Source::Local.new
+
+ if local_spec = local_source.find_gem(name, dep.requirement) then
+ res << Gem::Resolver::IndexSpecification.new(
+ self, local_spec.name, local_spec.version,
+ local_source, local_spec.platform)
+ end
+ end
+
+ res.delete_if do |spec|
+ spec.version.prerelease? and not dep.prerelease?
+ end
+
+ res.concat @remote_set.find_all req if consider_remote?
+
+ res
+ end
+
+ def prefetch(reqs)
+ @remote_set.prefetch(reqs) if consider_remote?
+ end
+
+ def prerelease= allow_prerelease
+ super
+
+ @remote_set.prerelease = allow_prerelease
+ end
+
+ def inspect # :nodoc:
+ always_install = @always_install.map { |s| s.full_name }
+
+ '#<%s domain: %s specs: %p always install: %p>' % [
+ self.class, @domain, @specs.keys, always_install,
+ ]
+ end
+
+ ##
+ # Called from IndexSpecification to get a true Specification
+ # object.
+
+ def load_spec name, ver, platform, source # :nodoc:
+ key = "#{name}-#{ver}-#{platform}"
+
+ @specs.fetch key do
+ tuple = Gem::NameTuple.new name, ver, platform
+
+ @specs[key] = source.fetch_spec tuple
+ end
+ end
+
+ ##
+ # Has a local gem for +dep_name+ been added to this set?
+
+ def local? dep_name # :nodoc:
+ spec, = @local[dep_name]
+
+ spec
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[InstallerSet', ']' do
+ q.breakable
+ q.text "domain: #{@domain}"
+
+ q.breakable
+ q.text 'specs: '
+ q.pp @specs.keys
+
+ q.breakable
+ q.text 'always install: '
+ q.pp @always_install
+ end
+ end
+
+ def remote= remote # :nodoc:
+ case @domain
+ when :local then
+ @domain = :both if remote
+ when :remote then
+ @domain = nil unless remote
+ when :both then
+ @domain = :local unless remote
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/local_specification.rb b/jni/ruby/lib/rubygems/resolver/local_specification.rb
new file mode 100644
index 0000000..20a283f
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/local_specification.rb
@@ -0,0 +1,41 @@
+##
+# A LocalSpecification comes from a .gem file on the local filesystem.
+
+class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
+
+ ##
+ # Returns +true+ if this gem is installable for the current platform.
+
+ def installable_platform?
+ return true if @source.kind_of? Gem::Source::SpecificFile
+
+ super
+ end
+
+ def local? # :nodoc:
+ true
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LocalSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp dependencies
+
+ q.breakable
+ q.text "source: #{@source.path}"
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/lock_set.rb b/jni/ruby/lib/rubygems/resolver/lock_set.rb
new file mode 100644
index 0000000..4ede597
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/lock_set.rb
@@ -0,0 +1,84 @@
+##
+# A set of gems from a gem dependencies lockfile.
+
+class Gem::Resolver::LockSet < Gem::Resolver::Set
+
+ attr_reader :specs # :nodoc:
+
+ ##
+ # Creates a new LockSet from the given +sources+
+
+ def initialize sources
+ super()
+
+ @sources = sources.map do |source|
+ Gem::Source::Lock.new source
+ end
+
+ @specs = []
+ end
+
+ ##
+ # Creates a new IndexSpecification in this set using the given +name+,
+ # +version+ and +platform+.
+ #
+ # The specification's set will be the current set, and the source will be
+ # the current set's source.
+
+ def add name, version, platform # :nodoc:
+ version = Gem::Version.new version
+
+ specs = @sources.map do |source|
+ Gem::Resolver::LockSpecification.new self, name, version, source,
+ platform
+ end
+
+ @specs.concat specs
+
+ specs
+ end
+
+ ##
+ # Returns an Array of IndexSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.select do |spec|
+ req.match? spec
+ end
+ end
+
+ ##
+ # Loads a Gem::Specification with the given +name+, +version+ and
+ # +platform+. +source+ is ignored.
+
+ def load_spec name, version, platform, source # :nodoc:
+ dep = Gem::Dependency.new name, version
+
+ found = @specs.find do |spec|
+ dep.matches_spec? spec and spec.platform == platform
+ end
+
+ tuple = Gem::NameTuple.new found.name, found.version, found.platform
+
+ found.source.fetch_spec tuple
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LockSet', ']' do
+ q.breakable
+ q.text 'source:'
+
+ q.breakable
+ q.pp @source
+
+ q.breakable
+ q.text 'specs:'
+
+ q.breakable
+ q.pp @specs.map { |spec| spec.full_name }
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/lock_specification.rb b/jni/ruby/lib/rubygems/resolver/lock_specification.rb
new file mode 100644
index 0000000..0013171
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/lock_specification.rb
@@ -0,0 +1,84 @@
+##
+# The LockSpecification comes from a lockfile (Gem::RequestSet::Lockfile).
+#
+# A LockSpecification's dependency information is pre-filled from the
+# lockfile.
+
+class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
+
+ def initialize set, name, version, source, platform
+ super()
+
+ @name = name
+ @platform = platform
+ @set = set
+ @source = source
+ @version = version
+
+ @dependencies = []
+ @spec = nil
+ end
+
+ ##
+ # This is a null install as a locked specification is considered installed.
+ # +options+ are ignored.
+
+ def install options = {}
+ destination = options[:install_dir] || Gem.dir
+
+ if File.exist? File.join(destination, 'specifications', spec.spec_name) then
+ yield nil
+ return
+ end
+
+ super
+ end
+
+ ##
+ # Adds +dependency+ from the lockfile to this specification
+
+ def add_dependency dependency # :nodoc:
+ @dependencies << dependency
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LockSpecification', ']' do
+ q.breakable
+ q.text "name: #{@name}"
+
+ q.breakable
+ q.text "version: #{@version}"
+
+ unless @platform == Gem::Platform::RUBY then
+ q.breakable
+ q.text "platform: #{@platform}"
+ end
+
+ unless @dependencies.empty? then
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp @dependencies
+ end
+ end
+ end
+
+ ##
+ # A specification constructed from the lockfile is returned
+
+ def spec
+ @spec ||= Gem::Specification.find { |spec|
+ spec.name == @name and spec.version == @version
+ }
+
+ @spec ||= Gem::Specification.new do |s|
+ s.name = @name
+ s.version = @version
+ s.platform = @platform
+
+ s.dependencies.concat @dependencies
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/requirement_list.rb b/jni/ruby/lib/rubygems/resolver/requirement_list.rb
new file mode 100644
index 0000000..a6bfaab
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/requirement_list.rb
@@ -0,0 +1,81 @@
+##
+# The RequirementList is used to hold the requirements being considered
+# while resolving a set of gems.
+#
+# The RequirementList acts like a queue where the oldest items are removed
+# first.
+
+class Gem::Resolver::RequirementList
+
+ include Enumerable
+
+ ##
+ # Creates a new RequirementList.
+
+ def initialize
+ @exact = []
+ @list = []
+ end
+
+ def initialize_copy other # :nodoc:
+ @exact = @exact.dup
+ @list = @list.dup
+ end
+
+ ##
+ # Adds Resolver::DependencyRequest +req+ to this requirements list.
+
+ def add(req)
+ if req.requirement.exact?
+ @exact.push req
+ else
+ @list.push req
+ end
+ req
+ end
+
+ ##
+ # Enumerates requirements in the list
+
+ def each # :nodoc:
+ return enum_for __method__ unless block_given?
+
+ @exact.each do |requirement|
+ yield requirement
+ end
+
+ @list.each do |requirement|
+ yield requirement
+ end
+ end
+
+ ##
+ # How many elements are in the list
+
+ def size
+ @exact.size + @list.size
+ end
+
+ ##
+ # Is the list empty?
+
+ def empty?
+ @exact.empty? && @list.empty?
+ end
+
+ ##
+ # Remove the oldest DependencyRequest from the list.
+
+ def remove
+ return @exact.shift unless @exact.empty?
+ @list.shift
+ end
+
+ ##
+ # Returns the oldest five entries from the list.
+
+ def next5
+ x = @exact[0,5]
+ x + @list[0,5 - x.size]
+ end
+end
diff --git a/jni/ruby/lib/rubygems/resolver/set.rb b/jni/ruby/lib/rubygems/resolver/set.rb
new file mode 100644
index 0000000..b26dc45
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/set.rb
@@ -0,0 +1,56 @@
+##
+# Resolver sets are used to look up specifications (and their
+# dependencies) used in resolution. This set is abstract.
+
+class Gem::Resolver::Set
+
+ ##
+ # Set to true to disable network access for this set
+
+ attr_accessor :remote
+
+ ##
+ # Errors encountered when resolving gems
+
+ attr_accessor :errors
+
+ ##
+ # When true, allows matching of requests to prerelease gems.
+
+ attr_accessor :prerelease
+
+ def initialize # :nodoc:
+ @prerelease = false
+ @remote = true
+ @errors = []
+ end
+
+ ##
+ # The find_all method must be implemented. It returns all Resolver
+ # Specification objects matching the given DependencyRequest +req+.
+
+ def find_all req
+ raise NotImplementedError
+ end
+
+ ##
+ # The #prefetch method may be overridden, but this is not necessary. This
+ # default implementation does nothing, which is suitable for sets where
+ # looking up a specification is cheap (such as installed gems).
+ #
+ # When overridden, the #prefetch method should look up specifications
+ # matching +reqs+.
+
+ def prefetch reqs
+ end
+
+ ##
+ # When true, this set is allowed to access the network when looking up
+ # specifications or dependencies.
+
+ def remote? # :nodoc:
+ @remote
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/spec_specification.rb b/jni/ruby/lib/rubygems/resolver/spec_specification.rb
new file mode 100644
index 0000000..1350e8a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/spec_specification.rb
@@ -0,0 +1,56 @@
+##
+# The Resolver::SpecSpecification contains common functionality for
+# Resolver specifications that are backed by a Gem::Specification.
+
+class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
+
+ ##
+ # A SpecSpecification is created for a +set+ for a Gem::Specification in
+ # +spec+. The +source+ is either where the +spec+ came from, or should be
+ # loaded from.
+
+ def initialize set, spec, source = nil
+ @set = set
+ @source = source
+ @spec = spec
+ end
+
+ ##
+ # The dependencies of the gem for this specification
+
+ def dependencies
+ spec.dependencies
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{spec.name}-#{spec.version}"
+ end
+
+ ##
+ # The name of the gem for this specification
+
+ def name
+ spec.name
+ end
+
+ ##
+ # The platform this gem works on.
+
+ def platform
+ spec.platform
+ end
+
+ ##
+ # The version of the gem for this specification.
+
+ def version
+ spec.version
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/specification.rb b/jni/ruby/lib/rubygems/resolver/specification.rb
new file mode 100644
index 0000000..4d77293
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/specification.rb
@@ -0,0 +1,110 @@
+##
+# A Resolver::Specification contains a subset of the information
+# contained in a Gem::Specification. Only the information necessary for
+# dependency resolution in the resolver is included.
+
+class Gem::Resolver::Specification
+
+ ##
+ # The dependencies of the gem for this specification
+
+ attr_reader :dependencies
+
+ ##
+ # The name of the gem for this specification
+
+ attr_reader :name
+
+ ##
+ # The platform this gem works on.
+
+ attr_reader :platform
+
+ ##
+ # The set this specification came from.
+
+ attr_reader :set
+
+ ##
+ # The source for this specification
+
+ attr_reader :source
+
+ ##
+ # The Gem::Specification for this Resolver::Specification.
+ #
+ # Implementers, note that #install updates @spec, so be sure to cache the
+ # Gem::Specification in @spec when overriding.
+
+ attr_reader :spec
+
+ ##
+ # The version of the gem for this specification.
+
+ attr_reader :version
+
+ ##
+ # Sets default instance variables for the specification.
+
+ def initialize
+ @dependencies = nil
+ @name = nil
+ @platform = nil
+ @set = nil
+ @source = nil
+ @version = nil
+ end
+
+ ##
+ # Fetches development dependencies if the source does not provide them by
+ # default (see APISpecification).
+
+ def fetch_development_dependencies # :nodoc:
+ end
+
+ ##
+ # The name and version of the specification.
+ #
+ # Unlike Gem::Specification#full_name, the platform is not included.
+
+ def full_name
+ "#{@name}-#{@version}"
+ end
+
+ ##
+ # Installs this specification using the Gem::Installer +options+. The
+ # install method yields a Gem::Installer instance, which indicates the
+ # gem will be installed, or +nil+, which indicates the gem is already
+ # installed.
+ #
+ # After installation #spec is updated to point to the just-installed
+ # specification.
+
+ def install options = {}
+ require 'rubygems/installer'
+
+ destination = options[:install_dir] || Gem.dir
+
+ Gem.ensure_gem_subdirectories destination
+
+ gem = source.download spec, destination
+
+ installer = Gem::Installer.new gem, options
+
+ yield installer if block_given?
+
+ @spec = installer.install
+ end
+
+ ##
+ # Returns true if this specification is installable on this platform.
+
+ def installable_platform?
+ Gem::Platform.match spec.platform
+ end
+
+ def local? # :nodoc:
+ false
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/stats.rb b/jni/ruby/lib/rubygems/resolver/stats.rb
new file mode 100644
index 0000000..c31e5be
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/stats.rb
@@ -0,0 +1,44 @@
+class Gem::Resolver::Stats
+ def initialize
+ @max_depth = 0
+ @max_requirements = 0
+ @requirements = 0
+ @backtracking = 0
+ @iterations = 0
+ end
+
+ def record_depth(stack)
+ if stack.size > @max_depth
+ @max_depth = stack.size
+ end
+ end
+
+ def record_requirements(reqs)
+ if reqs.size > @max_requirements
+ @max_requirements = reqs.size
+ end
+ end
+
+ def requirement!
+ @requirements += 1
+ end
+
+ def backtracking!
+ @backtracking += 1
+ end
+
+ def iteration!
+ @iterations += 1
+ end
+
+ PATTERN = "%20s: %d\n"
+
+ def display
+ $stdout.puts "=== Resolver Statistics ==="
+ $stdout.printf PATTERN, "Max Depth", @max_depth
+ $stdout.printf PATTERN, "Total Requirements", @requirements
+ $stdout.printf PATTERN, "Max Requirements", @max_requirements
+ $stdout.printf PATTERN, "Backtracking #", @backtracking
+ $stdout.printf PATTERN, "Iteration #", @iterations
+ end
+end
diff --git a/jni/ruby/lib/rubygems/resolver/vendor_set.rb b/jni/ruby/lib/rubygems/resolver/vendor_set.rb
new file mode 100644
index 0000000..614bd05
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/vendor_set.rb
@@ -0,0 +1,87 @@
+##
+# A VendorSet represents gems that have been unpacked into a specific
+# directory that contains a gemspec.
+#
+# This is used for gem dependency file support.
+#
+# Example:
+#
+# set = Gem::Resolver::VendorSet.new
+#
+# set.add_vendor_gem 'rake', 'vendor/rake'
+#
+# The directory vendor/rake must contain an unpacked rake gem along with a
+# rake.gemspec (watching the given name).
+
+class Gem::Resolver::VendorSet < Gem::Resolver::Set
+
+ ##
+ # The specifications for this set.
+
+ attr_reader :specs # :nodoc:
+
+ def initialize # :nodoc:
+ super()
+
+ @directories = {}
+ @specs = {}
+ end
+
+ ##
+ # Adds a specification to the set with the given +name+ which has been
+ # unpacked into the given +directory+.
+
+ def add_vendor_gem name, directory # :nodoc:
+ gemspec = File.join directory, "#{name}.gemspec"
+
+ spec = Gem::Specification.load gemspec
+
+ raise Gem::GemNotFoundException,
+ "unable to find #{gemspec} for gem #{name}" unless spec
+
+ spec.full_gem_path = File.expand_path directory
+
+ @specs[spec.name] = spec
+ @directories[spec] = directory
+
+ spec
+ end
+
+ ##
+ # Returns an Array of VendorSpecification objects matching the
+ # DependencyRequest +req+.
+
+ def find_all req
+ @specs.values.select do |spec|
+ req.match? spec
+ end.map do |spec|
+ source = Gem::Source::Vendor.new @directories[spec]
+ Gem::Resolver::VendorSpecification.new self, spec, source
+ end
+ end
+
+ ##
+ # Loads a spec with the given +name+. +version+, +platform+ and +source+ are
+ # ignored.
+
+ def load_spec name, version, platform, source # :nodoc:
+ @specs.fetch name
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[VendorSet', ']' do
+ next if @directories.empty?
+ q.breakable
+
+ dirs = @directories.map do |spec, directory|
+ "#{spec.full_name}: #{directory}"
+ end
+
+ q.seplist dirs do |dir|
+ q.text dir
+ end
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/resolver/vendor_specification.rb b/jni/ruby/lib/rubygems/resolver/vendor_specification.rb
new file mode 100644
index 0000000..a99b5f3
--- /dev/null
+++ b/jni/ruby/lib/rubygems/resolver/vendor_specification.rb
@@ -0,0 +1,24 @@
+##
+# A VendorSpecification represents a gem that has been unpacked into a project
+# and is being loaded through a gem dependencies file through the +path:+
+# option.
+
+class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
+
+ def == other # :nodoc:
+ self.class === other and
+ @set == other.set and
+ @spec == other.spec and
+ @source == other.source
+ end
+
+ ##
+ # This is a null install as this gem was unpacked into a directory.
+ # +options+ are ignored.
+
+ def install options = {}
+ yield nil
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/security.rb b/jni/ruby/lib/rubygems/security.rb
new file mode 100644
index 0000000..8c5fb7d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/security.rb
@@ -0,0 +1,595 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/exceptions'
+require 'fileutils'
+
+begin
+ require 'openssl'
+rescue LoadError => e
+ raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
+ e.message =~ / -- openssl$/
+end
+
+##
+# = Signing gems
+#
+# The Gem::Security implements cryptographic signatures for gems. The section
+# below is a step-by-step guide to using signed gems and generating your own.
+#
+# == Walkthrough
+#
+# === Building your certificate
+#
+# In order to start signing your gems, you'll need to build a private key and
+# a self-signed certificate. Here's how:
+#
+# # build a private key and certificate for yourself:
+# $ gem cert --build you@example.com
+#
+# This could take anywhere from a few seconds to a minute or two, depending on
+# the speed of your computer (public key algorithms aren't exactly the
+# speediest crypto algorithms in the world). When it's finished, you'll see
+# the files "gem-private_key.pem" and "gem-public_cert.pem" in the current
+# directory.
+#
+# First things first: Move both files to ~/.gem if you don't already have a
+# key and certificate in that directory. Ensure the file permissions make the
+# key unreadable by others (by default the file is saved securely).
+#
+# Keep your private key hidden; if it's compromised, someone can sign packages
+# as you (note: PKI has ways of mitigating the risk of stolen keys; more on
+# that later).
+#
+# === Signing Gems
+#
+# In RubyGems 2 and newer there is no extra work to sign a gem. RubyGems will
+# automatically find your key and certificate in your home directory and use
+# them to sign newly packaged gems.
+#
+# If your certificate is not self-signed (signed by a third party) RubyGems
+# will attempt to load the certificate chain from the trusted certificates.
+# Use <code>gem cert --add signing_cert.pem</code> to add your signers as
+# trusted certificates. See below for further information on certificate
+# chains.
+#
+# If you build your gem it will automatically be signed. If you peek inside
+# your gem file, you'll see a couple of new files have been added:
+#
+# $ tar tf your-gem-1.0.gem
+# metadata.gz
+# metadata.gz.sum
+# metadata.gz.sig # metadata signature
+# data.tar.gz
+# data.tar.gz.sum
+# data.tar.gz.sig # data signature
+#
+# === Manually signing gems
+#
+# If you wish to store your key in a separate secure location you'll need to
+# set your gems up for signing by hand. To do this, set the
+# <code>signing_key</code> and <code>cert_chain</code> in the gemspec before
+# packaging your gem:
+#
+# s.signing_key = '/secure/path/to/gem-private_key.pem'
+# s.cert_chain = %w[/secure/path/to/gem-public_cert.pem]
+#
+# When you package your gem with these options set RubyGems will automatically
+# load your key and certificate from the secure paths.
+#
+# === Signed gems and security policies
+#
+# Now let's verify the signature. Go ahead and install the gem, but add the
+# following options: <code>-P HighSecurity</code>, like this:
+#
+# # install the gem with using the security policy "HighSecurity"
+# $ sudo gem install your.gem -P HighSecurity
+#
+# The <code>-P</code> option sets your security policy -- we'll talk about
+# that in just a minute. Eh, what's this?
+#
+# $ gem install -P HighSecurity your-gem-1.0.gem
+# ERROR: While executing gem ... (Gem::Security::Exception)
+# root cert /CN=you/DC=example is not trusted
+#
+# The culprit here is the security policy. RubyGems has several different
+# security policies. Let's take a short break and go over the security
+# policies. Here's a list of the available security policies, and a brief
+# description of each one:
+#
+# * NoSecurity - Well, no security at all. Signed packages are treated like
+# unsigned packages.
+# * LowSecurity - Pretty much no security. If a package is signed then
+# RubyGems will make sure the signature matches the signing
+# certificate, and that the signing certificate hasn't expired, but
+# that's it. A malicious user could easily circumvent this kind of
+# security.
+# * MediumSecurity - Better than LowSecurity and NoSecurity, but still
+# fallible. Package contents are verified against the signing
+# certificate, and the signing certificate is checked for validity,
+# and checked against the rest of the certificate chain (if you don't
+# know what a certificate chain is, stay tuned, we'll get to that).
+# The biggest improvement over LowSecurity is that MediumSecurity
+# won't install packages that are signed by untrusted sources.
+# Unfortunately, MediumSecurity still isn't totally secure -- a
+# malicious user can still unpack the gem, strip the signatures, and
+# distribute the gem unsigned.
+# * HighSecurity - Here's the bugger that got us into this mess.
+# The HighSecurity policy is identical to the MediumSecurity policy,
+# except that it does not allow unsigned gems. A malicious user
+# doesn't have a whole lot of options here; they can't modify the
+# package contents without invalidating the signature, and they can't
+# modify or remove signature or the signing certificate chain, or
+# RubyGems will simply refuse to install the package. Oh well, maybe
+# they'll have better luck causing problems for CPAN users instead :).
+#
+# The reason RubyGems refused to install your shiny new signed gem was because
+# it was from an untrusted source. Well, your code is infallible (naturally),
+# so you need to add yourself as a trusted source:
+#
+# # add trusted certificate
+# gem cert --add ~/.gem/gem-public_cert.pem
+#
+# You've now added your public certificate as a trusted source. Now you can
+# install packages signed by your private key without any hassle. Let's try
+# the install command above again:
+#
+# # install the gem with using the HighSecurity policy (and this time
+# # without any shenanigans)
+# $ gem install -P HighSecurity your-gem-1.0.gem
+# Successfully installed your-gem-1.0
+# 1 gem installed
+#
+# This time RubyGems will accept your signed package and begin installing.
+#
+# While you're waiting for RubyGems to work it's magic, have a look at some of
+# the other security commands by running <code>gem help cert</code>:
+#
+# Options:
+# -a, --add CERT Add a trusted certificate.
+# -l, --list [FILTER] List trusted certificates where the
+# subject contains FILTER
+# -r, --remove FILTER Remove trusted certificates where the
+# subject contains FILTER
+# -b, --build EMAIL_ADDR Build private key and self-signed
+# certificate for EMAIL_ADDR
+# -C, --certificate CERT Signing certificate for --sign
+# -K, --private-key KEY Key for --sign or --build
+# -s, --sign CERT Signs CERT with the key from -K
+# and the certificate from -C
+#
+# We've already covered the <code>--build</code> option, and the
+# <code>--add</code>, <code>--list</code>, and <code>--remove</code> commands
+# seem fairly straightforward; they allow you to add, list, and remove the
+# certificates in your trusted certificate list. But what's with this
+# <code>--sign</code> option?
+#
+# === Certificate chains
+#
+# To answer that question, let's take a look at "certificate chains", a
+# concept I mentioned earlier. There are a couple of problems with
+# self-signed certificates: first of all, self-signed certificates don't offer
+# a whole lot of security. Sure, the certificate says Yukihiro Matsumoto, but
+# how do I know it was actually generated and signed by matz himself unless he
+# gave me the certificate in person?
+#
+# The second problem is scalability. Sure, if there are 50 gem authors, then
+# I have 50 trusted certificates, no problem. What if there are 500 gem
+# authors? 1000? Having to constantly add new trusted certificates is a
+# pain, and it actually makes the trust system less secure by encouraging
+# RubyGems users to blindly trust new certificates.
+#
+# Here's where certificate chains come in. A certificate chain establishes an
+# arbitrarily long chain of trust between an issuing certificate and a child
+# certificate. So instead of trusting certificates on a per-developer basis,
+# we use the PKI concept of certificate chains to build a logical hierarchy of
+# trust. Here's a hypothetical example of a trust hierarchy based (roughly)
+# on geography:
+#
+# --------------------------
+# | rubygems@rubygems.org |
+# --------------------------
+# |
+# -----------------------------------
+# | |
+# ---------------------------- -----------------------------
+# | seattlerb@seattlerb.org | | dcrubyists@richkilmer.com |
+# ---------------------------- -----------------------------
+# | | | |
+# --------------- ---------------- ----------- --------------
+# | drbrain | | zenspider | | pabs@dc | | tomcope@dc |
+# --------------- ---------------- ----------- --------------
+#
+#
+# Now, rather than having 4 trusted certificates (one for drbrain, zenspider,
+# pabs@dc, and tomecope@dc), a user could actually get by with one
+# certificate, the "rubygems@rubygems.org" certificate.
+#
+# Here's how it works:
+#
+# I install "rdoc-3.12.gem", a package signed by "drbrain". I've never heard
+# of "drbrain", but his certificate has a valid signature from the
+# "seattle.rb@seattlerb.org" certificate, which in turn has a valid signature
+# from the "rubygems@rubygems.org" certificate. Voila! At this point, it's
+# much more reasonable for me to trust a package signed by "drbrain", because
+# I can establish a chain to "rubygems@rubygems.org", which I do trust.
+#
+# === Signing certificates
+#
+# The <code>--sign</code> option allows all this to happen. A developer
+# creates their build certificate with the <code>--build</code> option, then
+# has their certificate signed by taking it with them to their next regional
+# Ruby meetup (in our hypothetical example), and it's signed there by the
+# person holding the regional RubyGems signing certificate, which is signed at
+# the next RubyConf by the holder of the top-level RubyGems certificate. At
+# each point the issuer runs the same command:
+#
+# # sign a certificate with the specified key and certificate
+# # (note that this modifies client_cert.pem!)
+# $ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem
+# --sign client_cert.pem
+#
+# Then the holder of issued certificate (in this case, your buddy "drbrain"),
+# can start using this signed certificate to sign RubyGems. By the way, in
+# order to let everyone else know about his new fancy signed certificate,
+# "drbrain" would save his newly signed certificate as
+# <code>~/.gem/gem-public_cert.pem</code>
+#
+# Obviously this RubyGems trust infrastructure doesn't exist yet. Also, in
+# the "real world", issuers actually generate the child certificate from a
+# certificate request, rather than sign an existing certificate. And our
+# hypothetical infrastructure is missing a certificate revocation system.
+# These are that can be fixed in the future...
+#
+# At this point you should know how to do all of these new and interesting
+# things:
+#
+# * build a gem signing key and certificate
+# * adjust your security policy
+# * modify your trusted certificate list
+# * sign a certificate
+#
+# == Manually verifying signatures
+#
+# In case you don't trust RubyGems you can verify gem signatures manually:
+#
+# 1. Fetch and unpack the gem
+#
+# gem fetch some_signed_gem
+# tar -xf some_signed_gem-1.0.gem
+#
+# 2. Grab the public key from the gemspec
+#
+# gem spec some_signed_gem-1.0.gem cert_chain | \
+# ruby -ryaml -e 'puts YAML.load_documents($stdin)' > public_key.crt
+#
+# 3. Generate a SHA1 hash of the data.tar.gz
+#
+# openssl dgst -sha1 < data.tar.gz > my.hash
+#
+# 4. Verify the signature
+#
+# openssl rsautl -verify -inkey public_key.crt -certin \
+# -in data.tar.gz.sig > verified.hash
+#
+# 5. Compare your hash to the verified hash
+#
+# diff -s verified.hash my.hash
+#
+# 6. Repeat 5 and 6 with metadata.gz
+#
+# == OpenSSL Reference
+#
+# The .pem files generated by --build and --sign are PEM files. Here's a
+# couple of useful OpenSSL commands for manipulating them:
+#
+# # convert a PEM format X509 certificate into DER format:
+# # (note: Windows .cer files are X509 certificates in DER format)
+# $ openssl x509 -in input.pem -outform der -out output.der
+#
+# # print out the certificate in a human-readable format:
+# $ openssl x509 -in input.pem -noout -text
+#
+# And you can do the same thing with the private key file as well:
+#
+# # convert a PEM format RSA key into DER format:
+# $ openssl rsa -in input_key.pem -outform der -out output_key.der
+#
+# # print out the key in a human readable format:
+# $ openssl rsa -in input_key.pem -noout -text
+#
+# == Bugs/TODO
+#
+# * There's no way to define a system-wide trust list.
+# * custom security policies (from a YAML file, etc)
+# * Simple method to generate a signed certificate request
+# * Support for OCSP, SCVP, CRLs, or some other form of cert status check
+# (list is in order of preference)
+# * Support for encrypted private keys
+# * Some sort of semi-formal trust hierarchy (see long-winded explanation
+# above)
+# * Path discovery (for gem certificate chains that don't have a self-signed
+# root) -- by the way, since we don't have this, THE ROOT OF THE CERTIFICATE
+# CHAIN MUST BE SELF SIGNED if Policy#verify_root is true (and it is for the
+# MediumSecurity and HighSecurity policies)
+# * Better explanation of X509 naming (ie, we don't have to use email
+# addresses)
+# * Honor AIA field (see note about OCSP above)
+# * Honor extension restrictions
+# * Might be better to store the certificate chain as a PKCS#7 or PKCS#12
+# file, instead of an array embedded in the metadata.
+# * Flexible signature and key algorithms, not hard-coded to RSA and SHA1.
+#
+# == Original author
+#
+# Paul Duncan <pabs@pablotron.org>
+# http://pablotron.org/
+
+module Gem::Security
+
+ ##
+ # Gem::Security default exception type
+
+ class Exception < Gem::Exception; end
+
+ ##
+ # Digest algorithm used to sign gems
+
+ DIGEST_ALGORITHM =
+ if defined?(OpenSSL::Digest) then
+ OpenSSL::Digest::SHA1
+ end
+
+ ##
+ # Used internally to select the signing digest from all computed digests
+
+ DIGEST_NAME = # :nodoc:
+ if DIGEST_ALGORITHM then
+ DIGEST_ALGORITHM.new.name
+ end
+
+ ##
+ # Algorithm for creating the key pair used to sign gems
+
+ KEY_ALGORITHM =
+ if defined?(OpenSSL::PKey) then
+ OpenSSL::PKey::RSA
+ end
+
+ ##
+ # Length of keys created by KEY_ALGORITHM
+
+ KEY_LENGTH = 2048
+
+ ##
+ # Cipher used to encrypt the key pair used to sign gems.
+ # Must be in the list returned by OpenSSL::Cipher.ciphers
+
+ KEY_CIPHER = OpenSSL::Cipher.new('AES-256-CBC') if defined?(OpenSSL::Cipher)
+
+ ##
+ # One year in seconds
+
+ ONE_YEAR = 86400 * 365
+
+ ##
+ # The default set of extensions are:
+ #
+ # * The certificate is not a certificate authority
+ # * The key for the certificate may be used for key and data encipherment
+ # and digital signatures
+ # * The certificate contains a subject key identifier
+
+ EXTENSIONS = {
+ 'basicConstraints' => 'CA:FALSE',
+ 'keyUsage' =>
+ 'keyEncipherment,dataEncipherment,digitalSignature',
+ 'subjectKeyIdentifier' => 'hash',
+ }
+
+ def self.alt_name_or_x509_entry certificate, x509_entry
+ alt_name = certificate.extensions.find do |extension|
+ extension.oid == "#{x509_entry}AltName"
+ end
+
+ return alt_name.value if alt_name
+
+ certificate.send x509_entry
+ end
+
+ ##
+ # Creates an unsigned certificate for +subject+ and +key+. The lifetime of
+ # the key is from the current time to +age+ which defaults to one year.
+ #
+ # The +extensions+ restrict the key to the indicated uses.
+
+ def self.create_cert subject, key, age = ONE_YEAR, extensions = EXTENSIONS,
+ serial = 1
+ cert = OpenSSL::X509::Certificate.new
+
+ cert.public_key = key.public_key
+ cert.version = 2
+ cert.serial = serial
+
+ cert.not_before = Time.now
+ cert.not_after = Time.now + age
+
+ cert.subject = subject
+
+ ef = OpenSSL::X509::ExtensionFactory.new nil, cert
+
+ cert.extensions = extensions.map do |ext_name, value|
+ ef.create_extension ext_name, value
+ end
+
+ cert
+ end
+
+ ##
+ # Creates a self-signed certificate with an issuer and subject from +email+,
+ # a subject alternative name of +email+ and the given +extensions+ for the
+ # +key+.
+
+ def self.create_cert_email email, key, age = ONE_YEAR, extensions = EXTENSIONS
+ subject = email_to_name email
+
+ extensions = extensions.merge "subjectAltName" => "email:#{email}"
+
+ create_cert_self_signed subject, key, age, extensions
+ end
+
+ ##
+ # Creates a self-signed certificate with an issuer and subject of +subject+
+ # and the given +extensions+ for the +key+.
+
+ def self.create_cert_self_signed subject, key, age = ONE_YEAR,
+ extensions = EXTENSIONS, serial = 1
+ certificate = create_cert subject, key, age, extensions
+
+ sign certificate, key, certificate, age, extensions, serial
+ end
+
+ ##
+ # Creates a new key pair of the specified +length+ and +algorithm+. The
+ # default is a 2048 bit RSA key.
+
+ def self.create_key length = KEY_LENGTH, algorithm = KEY_ALGORITHM
+ algorithm.new length
+ end
+
+ ##
+ # Turns +email_address+ into an OpenSSL::X509::Name
+
+ def self.email_to_name email_address
+ email_address = email_address.gsub(/[^\w@.-]+/i, '_')
+
+ cn, dcs = email_address.split '@'
+
+ dcs = dcs.split '.'
+
+ name = "CN=#{cn}/#{dcs.map { |dc| "DC=#{dc}" }.join '/'}"
+
+ OpenSSL::X509::Name.parse name
+ end
+
+ ##
+ # Signs +expired_certificate+ with +private_key+ if the keys match and the
+ # expired certificate was self-signed.
+ #--
+ # TODO increment serial
+
+ def self.re_sign expired_certificate, private_key, age = ONE_YEAR,
+ extensions = EXTENSIONS
+ raise Gem::Security::Exception,
+ "incorrect signing key for re-signing " +
+ "#{expired_certificate.subject}" unless
+ expired_certificate.public_key.to_pem == private_key.public_key.to_pem
+
+ unless expired_certificate.subject.to_s ==
+ expired_certificate.issuer.to_s then
+ subject = alt_name_or_x509_entry expired_certificate, :subject
+ issuer = alt_name_or_x509_entry expired_certificate, :issuer
+
+ raise Gem::Security::Exception,
+ "#{subject} is not self-signed, contact #{issuer} " +
+ "to obtain a valid certificate"
+ end
+
+ serial = expired_certificate.serial + 1
+
+ create_cert_self_signed(expired_certificate.subject, private_key, age,
+ extensions, serial)
+ end
+
+ ##
+ # Resets the trust directory for verifying gems.
+
+ def self.reset
+ @trust_dir = nil
+ end
+
+ ##
+ # Sign the public key from +certificate+ with the +signing_key+ and
+ # +signing_cert+, using the Gem::Security::DIGEST_ALGORITHM. Uses the
+ # default certificate validity range and extensions.
+ #
+ # Returns the newly signed certificate.
+
+ def self.sign certificate, signing_key, signing_cert,
+ age = ONE_YEAR, extensions = EXTENSIONS, serial = 1
+ signee_subject = certificate.subject
+ signee_key = certificate.public_key
+
+ alt_name = certificate.extensions.find do |extension|
+ extension.oid == 'subjectAltName'
+ end
+
+ extensions = extensions.merge 'subjectAltName' => alt_name.value if
+ alt_name
+
+ issuer_alt_name = signing_cert.extensions.find do |extension|
+ extension.oid == 'subjectAltName'
+ end
+
+ extensions = extensions.merge 'issuerAltName' => issuer_alt_name.value if
+ issuer_alt_name
+
+ signed = create_cert signee_subject, signee_key, age, extensions, serial
+ signed.issuer = signing_cert.subject
+
+ signed.sign signing_key, Gem::Security::DIGEST_ALGORITHM.new
+ end
+
+ ##
+ # Returns a Gem::Security::TrustDir which wraps the directory where trusted
+ # certificates live.
+
+ def self.trust_dir
+ return @trust_dir if @trust_dir
+
+ dir = File.join Gem.user_home, '.gem', 'trust'
+
+ @trust_dir ||= Gem::Security::TrustDir.new dir
+ end
+
+ ##
+ # Enumerates the trusted certificates via Gem::Security::TrustDir.
+
+ def self.trusted_certificates &block
+ trust_dir.each_certificate(&block)
+ end
+
+ ##
+ # Writes +pemmable+, which must respond to +to_pem+ to +path+ with the given
+ # +permissions+. If passed +cipher+ and +passphrase+ those arguments will be
+ # passed to +to_pem+.
+
+ def self.write pemmable, path, permissions = 0600, passphrase = nil, cipher = KEY_CIPHER
+ path = File.expand_path path
+
+ open path, 'wb', permissions do |io|
+ if passphrase and cipher
+ io.write pemmable.to_pem cipher, passphrase
+ else
+ io.write pemmable.to_pem
+ end
+ end
+
+ path
+ end
+
+ reset
+
+end
+
+if defined?(OpenSSL::SSL) then
+ require 'rubygems/security/policy'
+ require 'rubygems/security/policies'
+ require 'rubygems/security/trust_dir'
+end
+
+require 'rubygems/security/signer'
+
diff --git a/jni/ruby/lib/rubygems/security/policies.rb b/jni/ruby/lib/rubygems/security/policies.rb
new file mode 100644
index 0000000..a976eca
--- /dev/null
+++ b/jni/ruby/lib/rubygems/security/policies.rb
@@ -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
+
diff --git a/jni/ruby/lib/rubygems/security/policy.rb b/jni/ruby/lib/rubygems/security/policy.rb
new file mode 100644
index 0000000..b9bcb17
--- /dev/null
+++ b/jni/ruby/lib/rubygems/security/policy.rb
@@ -0,0 +1,295 @@
+require 'rubygems/user_interaction'
+
+##
+# 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
+
+ include Gem::UserInteraction
+
+ 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 = {}
+ require 'openssl'
+
+ @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
+ raise Gem::Security::Exception, 'missing signing chain' unless chain
+ raise Gem::Security::Exception, 'empty signing chain' if chain.empty?
+
+ begin
+ 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
+ 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
+ raise Gem::Security::Exception, 'missing signing certificate' unless
+ signer
+
+ 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
+ unless signer and key then
+ return true unless @only_signed
+
+ raise Gem::Security::Exception, 'missing key or signature'
+ end
+
+ 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
+ raise Gem::Security::Exception, 'missing signing chain' unless chain
+
+ root = chain.first
+
+ raise Gem::Security::Exception, 'missing root certificate' unless root
+
+ 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
+ raise Gem::Security::Exception, 'missing signing chain' unless chain
+
+ root = chain.first
+
+ raise Gem::Security::Exception, 'missing root certificate' unless root
+
+ 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
+
+ ##
+ # Extracts the email or subject from +certificate+
+
+ def subject certificate # :nodoc:
+ certificate.extensions.each do |extension|
+ next unless extension.oid == 'subjectAltName'
+
+ return extension.value
+ end
+
+ certificate.subject.to_s
+ 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
+
+ ##
+ # For +full_name+, 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 = {},
+ full_name = '(unknown)'
+ if signatures.empty? then
+ if @only_signed then
+ raise Gem::Security::Exception,
+ "unsigned gems are not allowed by the #{name} policy"
+ elsif digests.empty? then
+ # lack of signatures is irrelevant if there is nothing to check
+ # against
+ else
+ alert_warning "#{full_name} is not signed"
+ return
+ end
+ 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
+
+ if @verify_data then
+ raise Gem::Security::Exception, 'no digests provided (probable bug)' if
+ signer_digests.nil? or signer_digests.empty?
+ else
+ signer_digests = {}
+ end
+
+ 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
+
+ if @only_trusted then
+ check_trust chain, digester, trust_dir
+ elsif signatures.empty? and digests.empty? then
+ # trust is irrelevant if there's no signatures to verify
+ else
+ alert_warning "#{subject signer} is not trusted for #{full_name}"
+ end
+
+ signatures.each do |file, _|
+ digest = signer_digests[file]
+
+ raise Gem::Security::Exception, "missing digest for #{file}" unless
+ digest
+ end
+
+ 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, spec.full_name
+
+ true
+ end
+
+ alias to_s name # :nodoc:
+
+end
+
diff --git a/jni/ruby/lib/rubygems/security/signer.rb b/jni/ruby/lib/rubygems/security/signer.rb
new file mode 100644
index 0000000..bb1eae7
--- /dev/null
+++ b/jni/ruby/lib/rubygems/security/signer.rb
@@ -0,0 +1,154 @@
+##
+# 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, passphrase = nil
+ @cert_chain = cert_chain
+ @key = key
+
+ unless @key then
+ default_key = File.join Gem.default_key_path
+ @key = default_key if File.exist? default_key
+ end
+
+ unless @cert_chain then
+ default_cert = File.join Gem.default_cert_path
+ @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), passphrase 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
+
+ ##
+ # Extracts the full name of +cert+. If the certificate has a subjectAltName
+ # this value is preferred, otherwise the subject is used.
+
+ def extract_name cert # :nodoc:
+ subject_alt_name = cert.extensions.find { |e| 'subjectAltName' == e.oid }
+
+ if subject_alt_name then
+ /\Aemail:/ =~ subject_alt_name.value
+
+ $' || subject_alt_name.value
+ else
+ cert.subject
+ 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
+
+ full_name = extract_name @cert_chain.last
+
+ Gem::Security::SigningPolicy.verify @cert_chain, @key, {}, {}, full_name
+
+ @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.default_cert_path
+ disk_cert = File.read disk_cert_path rescue nil
+ disk_key =
+ File.read File.join(Gem.default_key_path) 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, ".gem", 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
+
diff --git a/jni/ruby/lib/rubygems/security/trust_dir.rb b/jni/ruby/lib/rubygems/security/trust_dir.rb
new file mode 100644
index 0000000..76ef89a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/security/trust_dir.rb
@@ -0,0 +1,118 @@
+##
+# The TrustDir manages the trusted certificates for gem signature
+# verification.
+
+class Gem::Security::TrustDir
+
+ ##
+ # Default permissions for the trust directory and its contents
+
+ DEFAULT_PERMISSIONS = {
+ :trust_dir => 0700,
+ :trusted_cert => 0600,
+ }
+
+ ##
+ # The directory where trusted certificates will be stored.
+
+ attr_reader :dir
+
+ ##
+ # Creates a new TrustDir using +dir+ where the directory and file
+ # permissions will be checked according to +permissions+
+
+ def initialize dir, permissions = DEFAULT_PERMISSIONS
+ @dir = dir
+ @permissions = permissions
+
+ @digester = Gem::Security::DIGEST_ALGORITHM
+ end
+
+ ##
+ # 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
+
diff --git a/jni/ruby/lib/rubygems/server.rb b/jni/ruby/lib/rubygems/server.rb
new file mode 100644
index 0000000..7655be2
--- /dev/null
+++ b/jni/ruby/lib/rubygems/server.rb
@@ -0,0 +1,868 @@
+require 'webrick'
+require 'zlib'
+require 'erb'
+
+require 'rubygems'
+require 'rubygems/rdoc'
+
+##
+# Gem::Server and allows users to serve gems for consumption by
+# `gem --remote-install`.
+#
+# gem_server starts an HTTP server on the given port and serves the following:
+# * "/" - Browsing of gem spec files for installed gems
+# * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index
+# * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs
+# name/version/platform index
+# * "/quick/" - Individual gemspecs
+# * "/gems" - Direct access to download the installable gems
+# * "/rdoc?q=" - Search for installed rdoc documentation
+#
+# == Usage
+#
+# gem_server = Gem::Server.new Gem.dir, 8089, false
+# gem_server.run
+#
+#--
+# TODO Refactor into a real WEBrick servlet to remove code duplication.
+
+class Gem::Server
+
+ attr_reader :spec_dirs
+
+ include ERB::Util
+ include Gem::UserInteraction
+
+ SEARCH = <<-SEARCH
+ <form class="headerSearch" name="headerSearchForm" method="get" action="/rdoc">
+ <div id="search" style="float:right">
+ <label for="q">Filter/Search</label>
+ <input id="q" type="text" style="width:10em" name="q">
+ <button type="submit" style="display:none"></button>
+ </div>
+ </form>
+ SEARCH
+
+ DOC_TEMPLATE = <<-'DOC_TEMPLATE'
+ <?xml version="1.0" encoding="iso-8859-1"?>
+ <!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>RubyGems Documentation Index</title>
+ <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
+ </head>
+ <body>
+ <div id="fileHeader">
+<%= SEARCH %>
+ <h1>RubyGems Documentation Index</h1>
+ </div>
+ <!-- banner header -->
+
+ <div id="bodyContent">
+ <div id="contextContent">
+ <div id="description">
+ <h1>Summary</h1>
+ <p>There are <%=values["gem_count"]%> gems installed:</p>
+ <p>
+ <%= values["specs"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
+ <h1>Gems</h1>
+
+ <dl>
+ <% values["specs"].each do |spec| %>
+ <dt>
+ <% if spec["first_name_entry"] then %>
+ <a name="<%=spec["name"]%>"></a>
+ <% end %>
+
+ <b><%=spec["name"]%> <%=spec["version"]%></b>
+
+ <% if spec["ri_installed"] then %>
+ <a href="<%=spec["doc_path"]%>">[rdoc]</a>
+ <% elsif spec["rdoc_installed"] then %>
+ <a href="<%=spec["doc_path"]%>">[rdoc]</a>
+ <% else %>
+ <span title="rdoc not installed">[rdoc]</span>
+ <% end %>
+
+ <% if spec["homepage"] then %>
+ <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
+ <% else %>
+ <span title="no homepage available">[www]</span>
+ <% end %>
+
+ <% if spec["has_deps"] then %>
+ - depends on
+ <%= spec["dependencies"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
+ <% end %>
+ </dt>
+ <dd>
+ <%=spec["summary"]%>
+ <% if spec["executables"] then %>
+ <br/>
+
+ <% if spec["only_one_executable"] then %>
+ Executable is
+ <% else %>
+ Executables are
+ <%end%>
+
+ <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
+
+ <%end%>
+ <br/>
+ <br/>
+ </dd>
+ <% end %>
+ </dl>
+
+ </div>
+ </div>
+ </div>
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+ </div>
+ </body>
+ </html>
+ DOC_TEMPLATE
+
+ # CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108
+ RDOC_CSS = <<-RDOC_CSS
+body {
+ font-family: Verdana,Arial,Helvetica,sans-serif;
+ font-size: 90%;
+ margin: 0;
+ margin-left: 40px;
+ padding: 0;
+ background: white;
+}
+
+h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
+h1 { font-size: 150%; }
+h2,h3,h4 { margin-top: 1em; }
+
+a { background: #eef; color: #039; text-decoration: none; }
+a:hover { background: #039; color: #eef; }
+
+/* Override the base stylesheets Anchor inside a table cell */
+td > a {
+ background: transparent;
+ color: #039;
+ text-decoration: none;
+}
+
+/* and inside a section title */
+.section-title > a {
+ background: transparent;
+ color: #eee;
+ text-decoration: none;
+}
+
+/* === Structural elements =================================== */
+
+div#index {
+ margin: 0;
+ margin-left: -40px;
+ padding: 0;
+ font-size: 90%;
+}
+
+
+div#index a {
+ margin-left: 0.7em;
+}
+
+div#index .section-bar {
+ margin-left: 0px;
+ padding-left: 0.7em;
+ background: #ccc;
+ font-size: small;
+}
+
+
+div#classHeader, div#fileHeader {
+ width: auto;
+ color: white;
+ padding: 0.5em 1.5em 0.5em 1.5em;
+ margin: 0;
+ margin-left: -40px;
+ border-bottom: 3px solid #006;
+}
+
+div#classHeader a, div#fileHeader a {
+ background: inherit;
+ color: white;
+}
+
+div#classHeader td, div#fileHeader td {
+ background: inherit;
+ color: white;
+}
+
+
+div#fileHeader {
+ background: #057;
+}
+
+div#classHeader {
+ background: #048;
+}
+
+
+.class-name-in-header {
+ font-size: 180%;
+ font-weight: bold;
+}
+
+
+div#bodyContent {
+ padding: 0 1.5em 0 1.5em;
+}
+
+div#description {
+ padding: 0.5em 1.5em;
+ background: #efefef;
+ border: 1px dotted #999;
+}
+
+div#description h1,h2,h3,h4,h5,h6 {
+ color: #125;;
+ background: transparent;
+}
+
+div#validator-badges {
+ text-align: center;
+}
+div#validator-badges img { border: 0; }
+
+div#copyright {
+ color: #333;
+ background: #efefef;
+ font: 0.75em sans-serif;
+ margin-top: 5em;
+ margin-bottom: 0;
+ padding: 0.5em 2em;
+}
+
+
+/* === Classes =================================== */
+
+table.header-table {
+ color: white;
+ font-size: small;
+}
+
+.type-note {
+ font-size: small;
+ color: #DEDEDE;
+}
+
+.xxsection-bar {
+ background: #eee;
+ color: #333;
+ padding: 3px;
+}
+
+.section-bar {
+ color: #333;
+ border-bottom: 1px solid #999;
+ margin-left: -20px;
+}
+
+
+.section-title {
+ background: #79a;
+ color: #eee;
+ padding: 3px;
+ margin-top: 2em;
+ margin-left: -30px;
+ border: 1px solid #999;
+}
+
+.top-aligned-row { vertical-align: top }
+.bottom-aligned-row { vertical-align: bottom }
+
+/* --- Context section classes ----------------------- */
+
+.context-row { }
+.context-item-name { font-family: monospace; font-weight: bold; color: black; }
+.context-item-value { font-size: small; color: #448; }
+.context-item-desc { color: #333; padding-left: 2em; }
+
+/* --- Method classes -------------------------- */
+.method-detail {
+ background: #efefef;
+ padding: 0;
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+ border: 1px dotted #ccc;
+}
+.method-heading {
+ color: black;
+ background: #ccc;
+ border-bottom: 1px solid #666;
+ padding: 0.2em 0.5em 0 0.5em;
+}
+.method-signature { color: black; background: inherit; }
+.method-name { font-weight: bold; }
+.method-args { font-style: italic; }
+.method-description { padding: 0 0.5em 0 0.5em; }
+
+/* --- Source code sections -------------------- */
+
+a.source-toggle { font-size: 90%; }
+div.method-source-code {
+ background: #262626;
+ color: #ffdead;
+ margin: 1em;
+ padding: 0.5em;
+ border: 1px dashed #999;
+ overflow: hidden;
+}
+
+div.method-source-code pre { color: #ffdead; overflow: hidden; }
+
+/* --- Ruby keyword styles --------------------- */
+
+.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
+
+.ruby-constant { color: #7fffd4; background: transparent; }
+.ruby-keyword { color: #00ffff; background: transparent; }
+.ruby-ivar { color: #eedd82; background: transparent; }
+.ruby-operator { color: #00ffee; background: transparent; }
+.ruby-identifier { color: #ffdead; background: transparent; }
+.ruby-node { color: #ffa07a; background: transparent; }
+.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
+.ruby-regexp { color: #ffa07a; background: transparent; }
+.ruby-value { color: #7fffd4; background: transparent; }
+ RDOC_CSS
+
+ RDOC_NO_DOCUMENTATION = <<-'NO_DOC'
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Found documentation</title>
+ <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
+ </head>
+ <body>
+ <div id="fileHeader">
+<%= SEARCH %>
+ <h1>No documentation found</h1>
+ </div>
+
+ <div id="bodyContent">
+ <div id="contextContent">
+ <div id="description">
+ <p>No gems matched <%= h query.inspect %></p>
+
+ <p>
+ Back to <a href="/">complete gem index</a>
+ </p>
+
+ </div>
+ </div>
+ </div>
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+ </div>
+ </body>
+</html>
+ NO_DOC
+
+ RDOC_SEARCH_TEMPLATE = <<-'RDOC_SEARCH'
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Found documentation</title>
+ <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
+ </head>
+ <body>
+ <div id="fileHeader">
+<%= SEARCH %>
+ <h1>Found documentation</h1>
+ </div>
+ <!-- banner header -->
+
+ <div id="bodyContent">
+ <div id="contextContent">
+ <div id="description">
+ <h1>Summary</h1>
+ <p><%=doc_items.length%> documentation topics found.</p>
+ <h1>Topics</h1>
+
+ <dl>
+ <% doc_items.each do |doc_item| %>
+ <dt>
+ <b><%=doc_item[:name]%></b>
+ <a href="<%=doc_item[:url]%>">[rdoc]</a>
+ </dt>
+ <dd>
+ <%=doc_item[:summary]%>
+ <br/>
+ <br/>
+ </dd>
+ <% end %>
+ </dl>
+
+ <p>
+ Back to <a href="/">complete gem index</a>
+ </p>
+
+ </div>
+ </div>
+ </div>
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+ </div>
+ </body>
+</html>
+ RDOC_SEARCH
+
+ def self.run(options)
+ new(options[:gemdir], options[:port], options[:daemon],
+ options[:launch], options[:addresses]).run
+ end
+
+ def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)
+ Gem::RDoc.load_rdoc
+ Socket.do_not_reverse_lookup = true
+
+ @gem_dirs = Array gem_dirs
+ @port = port
+ @daemon = daemon
+ @launch = launch
+ @addresses = addresses
+
+ logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
+ @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
+
+ @spec_dirs = @gem_dirs.map { |gem_dir| File.join gem_dir, 'specifications' }
+ @spec_dirs.reject! { |spec_dir| !File.directory? spec_dir }
+
+ reset_gems
+
+ @have_rdoc_4_plus = nil
+ end
+
+ def add_date res
+ res['date'] = @spec_dirs.map do |spec_dir|
+ File.stat(spec_dir).mtime
+ end.max
+ 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.0.0.preview2').satisfied_by? Gem::RDoc.rdoc_version
+ end
+
+ def latest_specs(req, res)
+ reset_gems
+
+ res['content-type'] = 'application/x-gzip'
+
+ add_date res
+
+ latest_specs = Gem::Specification.latest_specs
+
+ specs = latest_specs.sort.map do |spec|
+ platform = spec.original_platform || Gem::Platform::RUBY
+ [spec.name, spec.version, platform]
+ end
+
+ specs = Marshal.dump specs
+
+ if req.path =~ /\.gz$/ then
+ specs = Gem.gzip specs
+ res['content-type'] = 'application/x-gzip'
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = specs.length
+ else
+ res.body << specs
+ end
+ end
+
+ ##
+ # Creates server sockets based on the addresses option. If no addresses
+ # were given a server socket for all interfaces is created.
+
+ def listen addresses = @addresses
+ addresses = [nil] unless addresses
+
+ listeners = 0
+
+ addresses.each do |address|
+ begin
+ @server.listen address, @port
+ @server.listeners[listeners..-1].each do |listener|
+ host, port = listener.addr.values_at 2, 1
+ host = "[#{host}]" if host =~ /:/ # we don't reverse lookup
+ say "Server started at http://#{host}:#{port}"
+ end
+
+ listeners = @server.listeners.length
+ rescue SystemCallError
+ next
+ end
+ end
+
+ if @server.listeners.empty? then
+ say "Unable to start a server."
+ say "Check for running servers or your --bind and --port arguments"
+ terminate_interaction 1
+ end
+ end
+
+ def prerelease_specs req, res
+ reset_gems
+
+ res['content-type'] = 'application/x-gzip'
+
+ add_date res
+
+ specs = Gem::Specification.select do |spec|
+ spec.version.prerelease?
+ end.sort.map do |spec|
+ platform = spec.original_platform || Gem::Platform::RUBY
+ [spec.name, spec.version, platform]
+ end
+
+ specs = Marshal.dump specs
+
+ if req.path =~ /\.gz$/ then
+ specs = Gem.gzip specs
+ res['content-type'] = 'application/x-gzip'
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = specs.length
+ else
+ res.body << specs
+ end
+ end
+
+ def quick(req, res)
+ reset_gems
+
+ res['content-type'] = 'text/plain'
+ add_date res
+
+ case req.request_uri.path
+ when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+[^-]*?)(-.*?)?\.gemspec\.rz$| then
+ marshal_format, name, version, platform = $1, $2, $3, $4
+ specs = Gem::Specification.find_all_by_name name, version
+
+ selector = [name, version, platform].map(&:inspect).join ' '
+
+ platform = if platform then
+ Gem::Platform.new platform.sub(/^-/, '')
+ else
+ Gem::Platform::RUBY
+ end
+
+ specs = specs.select { |s| s.platform == platform }
+
+ if specs.empty? then
+ res.status = 404
+ res.body = "No gems found matching #{selector}"
+ elsif specs.length > 1 then
+ res.status = 500
+ res.body = "Multiple gems found matching #{selector}"
+ elsif marshal_format then
+ res['content-type'] = 'application/x-deflate'
+ res.body << Gem.deflate(Marshal.dump(specs.first))
+ end
+ else
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
+ end
+ end
+
+ def root(req, res)
+ reset_gems
+
+ add_date res
+
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
+ req.path == '/'
+
+ specs = []
+ total_file_count = 0
+
+ Gem::Specification.each do |spec|
+ total_file_count += spec.files.size
+ deps = spec.dependencies.map { |dep|
+ {
+ "name" => dep.name,
+ "type" => dep.type,
+ "version" => dep.requirement.to_s,
+ }
+ }
+
+ deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
+ deps.last["is_last"] = true unless deps.empty?
+
+ # executables
+ executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
+ executables = nil if executables.empty?
+ executables.last["is_last"] = true if executables
+
+ specs << {
+ "authors" => spec.authors.sort.join(", "),
+ "date" => spec.date.to_s,
+ "dependencies" => deps,
+ "doc_path" => doc_root(spec.full_name),
+ "executables" => executables,
+ "only_one_executable" => (executables && executables.size == 1),
+ "full_name" => spec.full_name,
+ "has_deps" => !deps.empty?,
+ "homepage" => spec.homepage,
+ "name" => spec.name,
+ "rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?,
+ "ri_installed" => Gem::RDoc.new(spec).ri_installed?,
+ "summary" => spec.summary,
+ "version" => spec.version.to_s,
+ }
+ end
+
+ specs << {
+ "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
+ "dependencies" => [],
+ "doc_path" => doc_root("rubygems-#{Gem::VERSION}"),
+ "executables" => [{"executable" => 'gem', "is_last" => true}],
+ "only_one_executable" => true,
+ "full_name" => "rubygems-#{Gem::VERSION}",
+ "has_deps" => false,
+ "homepage" => "http://docs.rubygems.org/",
+ "name" => 'rubygems',
+ "ri_installed" => true,
+ "summary" => "RubyGems itself",
+ "version" => Gem::VERSION,
+ }
+
+ specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
+ specs.last["is_last"] = true
+
+ # tag all specs with first_name_entry
+ last_spec = nil
+ specs.each do |spec|
+ is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
+ spec["first_name_entry"] = is_first
+ last_spec = spec
+ end
+
+ # create page from template
+ template = ERB.new(DOC_TEMPLATE)
+ res['content-type'] = 'text/html'
+
+ values = { "gem_count" => specs.size.to_s, "specs" => specs,
+ "total_file_count" => total_file_count.to_s }
+
+ # suppress 1.9.3dev warning about unused variable
+ values = values
+
+ result = template.result binding
+ res.body = result
+ end
+
+ ##
+ # Can be used for quick navigation to the rdoc documentation. You can then
+ # define a search shortcut for your browser. E.g. in Firefox connect
+ # 'shortcut:rdoc' to http://localhost:8808/rdoc?q=%s template. Then you can
+ # directly open the ActionPack documentation by typing 'rdoc actionp'. If
+ # there are multiple hits for the search term, they are presented as a list
+ # with links.
+ #
+ # Search algorithm aims for an intuitive search:
+ # 1. first try to find the gems and documentation folders which name
+ # starts with the search term
+ # 2. search for entries, that *contain* the search term
+ # 3. show all the gems
+ #
+ # If there is only one search hit, user is immediately redirected to the
+ # documentation for the particular gem, otherwise a list with results is
+ # shown.
+ #
+ # === Additional trick - install documentation for Ruby core
+ #
+ # Note: please adjust paths accordingly use for example 'locate yaml.rb' and
+ # 'gem environment' to identify directories, that are specific for your
+ # local installation
+ #
+ # 1. install Ruby sources
+ # cd /usr/src
+ # sudo apt-get source ruby
+ #
+ # 2. generate documentation
+ # rdoc -o /usr/lib/ruby/gems/1.8/doc/core/rdoc \
+ # /usr/lib/ruby/1.8 ruby1.8-1.8.7.72
+ #
+ # By typing 'rdoc core' you can now access the core documentation
+
+ def rdoc(req, res)
+ query = req.query['q']
+ show_rdoc_for_pattern("#{query}*", res) && return
+ show_rdoc_for_pattern("*#{query}*", res) && return
+
+ template = ERB.new RDOC_NO_DOCUMENTATION
+
+ res['content-type'] = 'text/html'
+ res.body = template.result binding
+ end
+
+ ##
+ # Updates the server to use the latest installed gems.
+
+ def reset_gems # :nodoc:
+ Gem::Specification.dirs = @gem_dirs
+ end
+
+ ##
+ # Returns true and prepares http response, if rdoc for the requested gem
+ # name pattern was found.
+ #
+ # The search is based on the file system content, not on the gems metadata.
+ # This allows additional documentation folders like 'core' for the Ruby core
+ # documentation - just put it underneath the main doc folder.
+
+ def show_rdoc_for_pattern(pattern, res)
+ found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select {|path|
+ File.exist? File.join(path, 'rdoc/index.html')
+ }
+ case found_gems.length
+ when 0
+ return false
+ when 1
+ new_path = File.basename(found_gems[0])
+ res.status = 302
+ res['Location'] = doc_root new_path
+ return true
+ else
+ doc_items = []
+ found_gems.each do |file_name|
+ base_name = File.basename(file_name)
+ doc_items << {
+ :name => base_name,
+ :url => doc_root(new_path),
+ :summary => ''
+ }
+ end
+
+ template = ERB.new(RDOC_SEARCH_TEMPLATE)
+ res['content-type'] = 'text/html'
+ result = template.result binding
+ res.body = result
+ return true
+ end
+ end
+
+ def run
+ listen
+
+ WEBrick::Daemon.start if @daemon
+
+ @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
+ @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
+
+ @server.mount_proc "/latest_specs.#{Gem.marshal_version}",
+ method(:latest_specs)
+ @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
+ method(:latest_specs)
+
+ @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}",
+ method(:prerelease_specs)
+ @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}.gz",
+ method(:prerelease_specs)
+
+ @server.mount_proc "/quick/", method(:quick)
+
+ @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
+ res['content-type'] = 'text/css'
+ add_date res
+ res.body << RDOC_CSS
+ end
+
+ @server.mount_proc "/", method(:root)
+
+ @server.mount_proc "/rdoc", method(:rdoc)
+
+ file_handlers = {
+ '/gems' => '/cache/',
+ }
+
+ 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
+
+ trap("INT") { @server.shutdown; exit! }
+ trap("TERM") { @server.shutdown; exit! }
+
+ launch if @launch
+
+ @server.start
+ end
+
+ def specs(req, res)
+ reset_gems
+
+ add_date res
+
+ specs = Gem::Specification.sort_by(&:sort_obj).map do |spec|
+ platform = spec.original_platform || Gem::Platform::RUBY
+ [spec.name, spec.version, platform]
+ end
+
+ specs = Marshal.dump specs
+
+ if req.path =~ /\.gz$/ then
+ specs = Gem.gzip specs
+ res['content-type'] = 'application/x-gzip'
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = specs.length
+ else
+ res.body << specs
+ end
+ end
+
+ def launch
+ listeners = @server.listeners.map{|l| l.addr[2] }
+
+ # TODO: 0.0.0.0 == any, not localhost.
+ host = listeners.any?{|l| l == '0.0.0.0'} ? 'localhost' : listeners.first
+
+ say "Launching browser to http://#{host}:#{@port}"
+
+ system("#{@launch} http://#{host}:#{@port}")
+ end
+end
diff --git a/jni/ruby/lib/rubygems/source.rb b/jni/ruby/lib/rubygems/source.rb
new file mode 100644
index 0000000..e5995f0
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source.rb
@@ -0,0 +1,234 @@
+require 'uri'
+require 'fileutils'
+
+##
+# A Source knows how to list and fetch gems from a RubyGems marshal index.
+#
+# There are other Source subclasses for installed gems, local gems, the
+# bundler dependency API and so-forth.
+
+class Gem::Source
+
+ include Comparable
+
+ FILES = { # :nodoc:
+ :released => 'specs',
+ :latest => 'latest_specs',
+ :prerelease => 'prerelease_specs',
+ }
+
+ ##
+ # The URI this source will fetch gems from.
+
+ attr_reader :uri
+
+ ##
+ # Creates a new Source which will use the index located at +uri+.
+
+ def initialize(uri)
+ begin
+ unless uri.kind_of? URI
+ uri = URI.parse(uri.to_s)
+ end
+ rescue URI::InvalidURIError
+ raise if Gem::Source == self.class
+ end
+
+ @uri = uri
+ @api_uri = nil
+ end
+
+ ##
+ # Use an SRV record on the host to look up the true endpoint for the index.
+
+ def api_uri # :nodoc:
+ require 'rubygems/remote_fetcher'
+ @api_uri ||= Gem::RemoteFetcher.fetcher.api_endpoint uri
+ end
+
+ ##
+ # Sources are ordered by installation preference.
+
+ def <=>(other)
+ case other
+ when Gem::Source::Installed,
+ Gem::Source::Local,
+ Gem::Source::Lock,
+ Gem::Source::SpecificFile,
+ Gem::Source::Git,
+ Gem::Source::Vendor then
+ -1
+ when Gem::Source then
+ if !@uri
+ return 0 unless other.uri
+ return 1
+ end
+
+ return -1 if !other.uri
+
+ @uri.to_s <=> other.uri.to_s
+ else
+ nil
+ end
+ end
+
+ def == other # :nodoc:
+ self.class === other and @uri == other.uri
+ end
+
+ alias_method :eql?, :== # :nodoc:
+
+ ##
+ # Returns a Set that can fetch specifications from this source.
+
+ def dependency_resolver_set # :nodoc:
+ return Gem::Resolver::IndexSet.new self if 'file' == api_uri.scheme
+
+ bundler_api_uri = api_uri + './api/v1/dependencies'
+
+ begin
+ fetcher = Gem::RemoteFetcher.fetcher
+ response = fetcher.fetch_path bundler_api_uri, nil, true
+ rescue Gem::RemoteFetcher::FetchError
+ Gem::Resolver::IndexSet.new self
+ else
+ if response.respond_to? :uri then
+ Gem::Resolver::APISet.new response.uri
+ else
+ Gem::Resolver::APISet.new bundler_api_uri
+ end
+ end
+ end
+
+ def hash # :nodoc:
+ @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-/')
+ escaped_path.untaint
+
+ File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
+ end
+
+ ##
+ # Returns true when it is possible and safe to update the cache directory.
+
+ def update_cache?
+ @update_cache ||=
+ begin
+ File.stat(Gem.user_home).uid == Process.uid
+ rescue Errno::ENOENT
+ false
+ end
+ end
+
+ ##
+ # Fetches a specification for the given +name_tuple+.
+
+ def fetch_spec name_tuple
+ fetcher = Gem::RemoteFetcher.fetcher
+
+ spec_file_name = name_tuple.spec_name
+
+ uri = api_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 = api_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, update_cache?
+
+ 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
+
+ ##
+ # Downloads +spec+ and writes it to +dir+. See also
+ # Gem::RemoteFetcher#download.
+
+ def download(spec, dir=Dir.pwd)
+ fetcher = Gem::RemoteFetcher.fetcher
+ fetcher.download spec, api_uri.to_s, dir
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Remote:', ']' do
+ q.breakable
+ q.text @uri.to_s
+
+ if api = api_uri
+ q.breakable
+ q.text 'API URI: '
+ q.text api.to_s
+ end
+ end
+ end
+
+end
+
+require 'rubygems/source/git'
+require 'rubygems/source/installed'
+require 'rubygems/source/specific_file'
+require 'rubygems/source/local'
+require 'rubygems/source/lock'
+require 'rubygems/source/vendor'
+
diff --git a/jni/ruby/lib/rubygems/source/git.rb b/jni/ruby/lib/rubygems/source/git.rb
new file mode 100644
index 0000000..35c2270
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source/git.rb
@@ -0,0 +1,240 @@
+require 'digest'
+require 'rubygems/util'
+
+##
+# A git gem for use in a gem dependencies file.
+#
+# Example:
+#
+# source =
+# Gem::Source::Git.new 'rake', 'git@example:rake.git', 'rake-10.1.0', false
+#
+# source.specs
+
+class Gem::Source::Git < Gem::Source
+
+ ##
+ # The name of the gem created by this git gem.
+
+ attr_reader :name
+
+ ##
+ # The commit reference used for checking out this git gem.
+
+ attr_reader :reference
+
+ ##
+ # When false the cache for this repository will not be updated.
+
+ attr_accessor :remote
+
+ ##
+ # The git repository this gem is sourced from.
+
+ attr_reader :repository
+
+ ##
+ # The directory for cache and git gem installation
+
+ attr_accessor :root_dir
+
+ ##
+ # Does this repository need submodules checked out too?
+
+ attr_reader :need_submodules
+
+ ##
+ # Creates a new git gem source for a gems from loaded from +repository+ at
+ # the given +reference+. The +name+ is only used to track the repository
+ # back to a gem dependencies file, it has no real significance as a git
+ # repository may contain multiple gems. If +submodules+ is true, submodules
+ # will be checked out when the gem is installed.
+
+ def initialize name, repository, reference, submodules = false
+ super repository
+
+ @name = name
+ @repository = repository
+ @reference = reference
+ @need_submodules = submodules
+
+ @remote = true
+ @root_dir = Gem.dir
+ @git = ENV['git'] || 'git'
+ end
+
+ def <=> other
+ case other
+ when Gem::Source::Git then
+ 0
+ when Gem::Source::Vendor,
+ Gem::Source::Lock then
+ -1
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
+ end
+
+ def == other # :nodoc:
+ super and
+ @name == other.name and
+ @repository == other.repository and
+ @reference == other.reference and
+ @need_submodules == other.need_submodules
+ end
+
+ ##
+ # Checks out the files for the repository into the install_dir.
+
+ def checkout # :nodoc:
+ cache
+
+ return false unless File.exist? repo_cache_dir
+
+ unless File.exist? install_dir then
+ system @git, 'clone', '--quiet', '--no-checkout',
+ repo_cache_dir, install_dir
+ end
+
+ Dir.chdir install_dir do
+ system @git, 'fetch', '--quiet', '--force', '--tags', install_dir
+
+ success = system @git, 'reset', '--quiet', '--hard', rev_parse
+
+ success &&=
+ Gem::Util.silent_system @git, 'submodule', 'update',
+ '--quiet', '--init', '--recursive' if @need_submodules
+
+ success
+ end
+ end
+
+ ##
+ # Creates a local cache repository for the git gem.
+
+ def cache # :nodoc:
+ return unless @remote
+
+ if File.exist? repo_cache_dir then
+ Dir.chdir repo_cache_dir do
+ system @git, 'fetch', '--quiet', '--force', '--tags',
+ @repository, 'refs/heads/*:refs/heads/*'
+ end
+ else
+ system @git, 'clone', '--quiet', '--bare', '--no-hardlinks',
+ @repository, repo_cache_dir
+ end
+ end
+
+ ##
+ # Directory where git gems get unpacked and so-forth.
+
+ def base_dir # :nodoc:
+ File.join @root_dir, 'bundler'
+ end
+
+ ##
+ # A short reference for use in git gem directories
+
+ def dir_shortref # :nodoc:
+ rev_parse[0..11]
+ end
+
+ ##
+ # Nothing to download for git gems
+
+ def download full_spec, path # :nodoc:
+ end
+
+ ##
+ # The directory where the git gem will be installed.
+
+ def install_dir # :nodoc:
+ return unless File.exist? repo_cache_dir
+
+ File.join base_dir, 'gems', "#{@name}-#{dir_shortref}"
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Git: ', ']' do
+ q.breakable
+ q.text @repository
+
+ q.breakable
+ q.text @reference
+ end
+ end
+
+ ##
+ # The directory where the git gem's repository will be cached.
+
+ def repo_cache_dir # :nodoc:
+ File.join @root_dir, 'cache', 'bundler', 'git', "#{@name}-#{uri_hash}"
+ end
+
+ ##
+ # Converts the git reference for the repository into a commit hash.
+
+ def rev_parse # :nodoc:
+ hash = nil
+
+ Dir.chdir repo_cache_dir do
+ hash = Gem::Util.popen(@git, 'rev-parse', @reference).strip
+ end
+
+ raise Gem::Exception,
+ "unable to find reference #{@reference} in #{@repository}" unless
+ $?.success?
+
+ hash
+ end
+
+ ##
+ # Loads all gemspecs in the repository
+
+ def specs
+ checkout
+
+ return [] unless install_dir
+
+ Dir.chdir install_dir do
+ Dir['{,*,*/*}.gemspec'].map do |spec_file|
+ directory = File.dirname spec_file
+ file = File.basename spec_file
+
+ Dir.chdir directory do
+ spec = Gem::Specification.load file
+ if spec then
+ spec.base_dir = base_dir
+
+ spec.extension_dir =
+ File.join base_dir, 'extensions', Gem::Platform.local.to_s,
+ Gem.extension_api_version, "#{name}-#{dir_shortref}"
+
+ spec.full_gem_path = File.dirname spec.loaded_from if spec
+ end
+ spec
+ end
+ end.compact
+ end
+ end
+
+ ##
+ # A hash for the git gem based on the git repository URI.
+
+ def uri_hash # :nodoc:
+ normalized =
+ if @repository =~ %r%^\w+://(\w+@)?% then
+ uri = URI(@repository).normalize.to_s.sub %r%/$%,''
+ uri.sub(/\A(\w+)/) { $1.downcase }
+ else
+ @repository
+ end
+
+ Digest::SHA1.hexdigest normalized
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/source/installed.rb b/jni/ruby/lib/rubygems/source/installed.rb
new file mode 100644
index 0000000..bd05c75
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source/installed.rb
@@ -0,0 +1,40 @@
+##
+# Represents an installed gem. This is used for dependency resolution.
+
+class Gem::Source::Installed < Gem::Source
+
+ def initialize # :nodoc:
+ @uri = nil
+ end
+
+ ##
+ # Installed sources sort before all other sources
+
+ def <=> other
+ case other
+ when Gem::Source::Git,
+ Gem::Source::Lock,
+ Gem::Source::Vendor then
+ -1
+ when Gem::Source::Installed then
+ 0
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
+ end
+
+ ##
+ # We don't need to download an installed gem
+
+ def download spec, path
+ nil
+ end
+
+ def pretty_print q # :nodoc:
+ q.text '[Installed]'
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/source/local.rb b/jni/ruby/lib/rubygems/source/local.rb
new file mode 100644
index 0000000..8057921
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source/local.rb
@@ -0,0 +1,129 @@
+##
+# The local source finds gems in the current directory for fulfilling
+# dependencies.
+
+class Gem::Source::Local < Gem::Source
+
+ def initialize # :nodoc:
+ @specs = nil
+ @api_uri = nil
+ @uri = nil
+ end
+
+ ##
+ # Local sorts before Gem::Source and after Gem::Source::Installed
+
+ def <=> other
+ case other
+ when Gem::Source::Installed,
+ Gem::Source::Lock then
+ -1
+ when Gem::Source::Local then
+ 0
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
+ end
+
+ def inspect # :nodoc:
+ keys = @specs ? @specs.keys.sort : 'NOT LOADED'
+ "#<%s specs: %p>" % [self.class, keys]
+ end
+
+ def load_specs type # :nodoc:
+ 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, # :nodoc:
+ 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.max_by { |s| s.version }
+ end
+
+ def fetch_spec name # :nodoc:
+ load_specs :complete
+
+ if data = @specs[name]
+ data.last.spec
+ else
+ raise Gem::Exception, "Unable to find spec for #{name.inspect}"
+ end
+ end
+
+ def download spec, cache_dir = nil # :nodoc:
+ 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
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[Local gems:', ']' do
+ q.breakable
+ q.seplist @specs.keys do |v|
+ q.text v.full_name
+ end
+ end
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/source/lock.rb b/jni/ruby/lib/rubygems/source/lock.rb
new file mode 100644
index 0000000..2ba7702
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source/lock.rb
@@ -0,0 +1,48 @@
+##
+# A Lock source wraps an installed gem's source and sorts before other sources
+# during dependency resolution. This allows RubyGems to prefer gems from
+# dependency lock files.
+
+class Gem::Source::Lock < Gem::Source
+
+ ##
+ # The wrapped Gem::Source
+
+ attr_reader :wrapped
+
+ ##
+ # Creates a new Lock source that wraps +source+ and moves it earlier in the
+ # sort list.
+
+ def initialize source
+ @wrapped = source
+ end
+
+ def <=> other # :nodoc:
+ case other
+ when Gem::Source::Lock then
+ @wrapped <=> other.wrapped
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
+ end
+
+ def == other # :nodoc:
+ 0 == (self <=> other)
+ end
+
+ ##
+ # Delegates to the wrapped source's fetch_spec method.
+
+ def fetch_spec name_tuple
+ @wrapped.fetch_spec name_tuple
+ end
+
+ def uri # :nodoc:
+ @wrapped.uri
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/source/specific_file.rb b/jni/ruby/lib/rubygems/source/specific_file.rb
new file mode 100644
index 0000000..250a839
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source/specific_file.rb
@@ -0,0 +1,72 @@
+##
+# A source representing a single .gem file. This is used for installation of
+# local gems.
+
+class Gem::Source::SpecificFile < Gem::Source
+
+ ##
+ # The path to the gem for this specific file.
+
+ attr_reader :path
+
+ ##
+ # Creates a new SpecificFile for the gem in +file+
+
+ def initialize(file)
+ @uri = nil
+ @path = ::File.expand_path(file)
+
+ @package = Gem::Package.new @path
+ @spec = @package.spec
+ @name = @spec.name_tuple
+ end
+
+ ##
+ # The Gem::Specification extracted from this .gem.
+
+ attr_reader :spec
+
+ def load_specs *a # :nodoc:
+ [@name]
+ end
+
+ def fetch_spec name # :nodoc:
+ return @spec if name == @name
+ raise Gem::Exception, "Unable to find '#{name}'"
+ @spec
+ end
+
+ def download spec, dir = nil # :nodoc:
+ return @path if spec == @spec
+ raise Gem::Exception, "Unable to download '#{spec.full_name}'"
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[SpecificFile:', ']' do
+ q.breakable
+ q.text @path
+ end
+ end
+
+ ##
+ # Orders this source against +other+.
+ #
+ # If +other+ is a SpecificFile from a different gem name +nil+ is returned.
+ #
+ # If +other+ is a SpecificFile from the same gem name the versions are
+ # compared using Gem::Version#<=>
+ #
+ # Otherwise Gem::Source#<=> is used.
+
+ def <=> other
+ case other
+ when Gem::Source::SpecificFile then
+ return nil if @spec.name != other.spec.name
+
+ @spec.version <=> other.spec.version
+ else
+ super
+ end
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/source/vendor.rb b/jni/ruby/lib/rubygems/source/vendor.rb
new file mode 100644
index 0000000..2d93623
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source/vendor.rb
@@ -0,0 +1,27 @@
+##
+# This represents a vendored source that is similar to an installed gem.
+
+class Gem::Source::Vendor < Gem::Source::Installed
+
+ ##
+ # Creates a new Vendor source for a gem that was unpacked at +path+.
+
+ def initialize path
+ @uri = path
+ end
+
+ def <=> other
+ case other
+ when Gem::Source::Lock then
+ -1
+ when Gem::Source::Vendor then
+ 0
+ when Gem::Source then
+ 1
+ else
+ nil
+ end
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/source_list.rb b/jni/ruby/lib/rubygems/source_list.rb
new file mode 100644
index 0000000..e01f11c
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source_list.rb
@@ -0,0 +1,149 @@
+require 'rubygems/source'
+
+##
+# The SourceList represents the sources rubygems has been configured to use.
+# A source may be created from an array of sources:
+#
+# Gem::SourceList.from %w[https://rubygems.example https://internal.example]
+#
+# Or by adding them:
+#
+# sources = Gem::SourceList.new
+# sources.add 'https://rubygems.example'
+#
+# The most common way to get a SourceList is Gem.sources.
+
+class Gem::SourceList
+
+ include Enumerable
+
+ ##
+ # Creates a new SourceList
+
+ def initialize
+ @sources = []
+ end
+
+ ##
+ # The sources in this list
+
+ attr_reader :sources
+
+ ##
+ # Creates a new SourceList from an array of sources.
+
+ def self.from(ary)
+ list = new
+
+ list.replace ary
+
+ return list
+ end
+
+ def initialize_copy(other) # :nodoc:
+ @sources = @sources.dup
+ end
+
+ ##
+ # Appends +obj+ to the source list which may be a Gem::Source, URI or URI
+ # String.
+
+ 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
+
+ ##
+ # Replaces this SourceList with the sources in +other+ See #<< for
+ # acceptable items in +other+.
+
+ def replace(other)
+ clear
+
+ other.each do |x|
+ self << x
+ end
+
+ self
+ end
+
+ ##
+ # Removes all sources from the SourceList.
+
+ def clear
+ @sources.clear
+ end
+
+ ##
+ # Yields each source URI in the list.
+
+ def each
+ @sources.each { |s| yield s.uri.to_s }
+ end
+
+ ##
+ # Yields each source in the list.
+
+ def each_source(&b)
+ @sources.each(&b)
+ end
+
+ ##
+ # Returns true if there are no sources in this SourceList.
+
+ def empty?
+ @sources.empty?
+ end
+
+ def == other # :nodoc:
+ to_a == other
+ end
+
+ ##
+ # Returns an Array of source URI Strings.
+
+ def to_a
+ @sources.map { |x| x.uri.to_s }
+ end
+
+ alias_method :to_ary, :to_a
+
+ ##
+ # Returns the first source in the list.
+
+ def first
+ @sources.first
+ end
+
+ ##
+ # Returns true if this source list includes +other+ which may be a
+ # Gem::Source or a source URI.
+
+ 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
+
+ ##
+ # Deletes +source+ from the source list which may be a Gem::Source or a URI.
+
+ def delete source
+ if source.kind_of? Gem::Source
+ @sources.delete source
+ else
+ @sources.delete_if { |x| x.uri.to_s == source.to_s }
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/source_local.rb b/jni/ruby/lib/rubygems/source_local.rb
new file mode 100644
index 0000000..0808f46
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source_local.rb
@@ -0,0 +1,5 @@
+require 'rubygems/source'
+require 'rubygems/source_local'
+
+# TODO warn upon require, this file is deprecated.
+
diff --git a/jni/ruby/lib/rubygems/source_specific_file.rb b/jni/ruby/lib/rubygems/source_specific_file.rb
new file mode 100644
index 0000000..f785c26
--- /dev/null
+++ b/jni/ruby/lib/rubygems/source_specific_file.rb
@@ -0,0 +1,4 @@
+require 'rubygems/source/specific_file'
+
+# TODO warn upon require, this file is deprecated.
+
diff --git a/jni/ruby/lib/rubygems/spec_fetcher.rb b/jni/ruby/lib/rubygems/spec_fetcher.rb
new file mode 100644
index 0000000..4967c4a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/spec_fetcher.rb
@@ -0,0 +1,269 @@
+require 'rubygems/remote_fetcher'
+require 'rubygems/user_interaction'
+require 'rubygems/errors'
+require 'rubygems/text'
+require 'rubygems/name_tuple'
+
+##
+# SpecFetcher handles metadata updates from remote gem repositories.
+
+class Gem::SpecFetcher
+
+ include Gem::UserInteraction
+ include Gem::Text
+
+ ##
+ # Cache of latest specs
+
+ attr_reader :latest_specs # :nodoc:
+
+ ##
+ # Sources for this SpecFetcher
+
+ attr_reader :sources # :nodoc:
+
+ ##
+ # Cache of all released specs
+
+ attr_reader :specs # :nodoc:
+
+ ##
+ # Cache of prerelease specs
+
+ attr_reader :prerelease_specs # :nodoc:
+
+ @fetcher = nil
+
+ ##
+ # Default fetcher instance. Use this instead of ::new to reduce object
+ # allocation.
+
+ def self.fetcher
+ @fetcher ||= new
+ end
+
+ def self.fetcher=(fetcher) # :nodoc:
+ @fetcher = fetcher
+ end
+
+ ##
+ # Creates a new SpecFetcher. Ordinarily you want to use the default fetcher
+ # from Gem::SpecFetcher::fetcher which uses the Gem.sources.
+ #
+ # If you need to retrieve specifications from a different +source+, you can
+ # send it as an argument.
+
+ def initialize sources = nil
+ @sources = sources || Gem.sources
+
+ @update_cache =
+ begin
+ File.stat(Gem.user_home).uid == Process.uid
+ rescue Errno::EACCES, Errno::ENOENT
+ false
+ end
+
+ @specs = {}
+ @latest_specs = {}
+ @prerelease_specs = {}
+
+ @caches = {
+ :latest => @latest_specs,
+ :prerelease => @prerelease_specs,
+ :released => @specs,
+ }
+
+ @fetcher = Gem::RemoteFetcher.fetcher
+ end
+
+ ##
+ #
+ # Find and fetch gem name tuples that match +dependency+.
+ #
+ # If +matching_platform+ is false, gems for all platforms are returned.
+
+ def search_for_dependency(dependency, matching_platform=true)
+ found = {}
+
+ rejected_specs = {}
+
+ if dependency.prerelease?
+ if dependency.specific?
+ type = :complete
+ else
+ type = :abs_latest
+ end
+ elsif dependency.latest_version?
+ type = :latest
+ else
+ type = :released
+ end
+
+ list, errors = available_specs(type)
+ list.each do |source, specs|
+ if dependency.name.is_a?(String) && specs.respond_to?(:bsearch)
+ start_index = (0 ... specs.length).bsearch{ |i| specs[i].name >= dependency.name }
+ end_index = (0 ... specs.length).bsearch{ |i| specs[i].name > dependency.name }
+ specs = specs[start_index ... end_index] if start_index && end_index
+ end
+
+ 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
+ else
+ true
+ end
+ end
+ end
+ end
+
+ errors += rejected_specs.values
+
+ tuples = []
+
+ found.each do |source, specs|
+ specs.each do |s|
+ tuples << [s, source]
+ end
+ end
+
+ tuples = tuples.sort_by { |x| x[0] }
+
+ return [tuples, errors]
+ end
+
+
+ ##
+ # 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
+
+ ##
+ # Suggests gems based on the supplied +gem_name+. Returns an array of
+ # alternative gem names.
+
+ def suggest_gems_from_name gem_name
+ gem_name = gem_name.downcase.tr('_-', '')
+ max = gem_name.size / 2
+ names = available_specs(:latest).first.values.flatten(1)
+
+ matches = names.map { |n|
+ next unless n.match_platform?
+
+ distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '')
+
+ next if distance >= max
+
+ return [n.name] if distance == 0
+
+ [n.name, distance]
+ }.compact
+
+ matches = matches.uniq.sort_by { |name, dist| dist }
+
+ matches.first(5).map { |name, dist| name }
+ end
+
+ ##
+ # Returns a list of gems available for each source in Gem::sources.
+ #
+ # +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 available_specs(type)
+ errors = []
+ list = {}
+
+ @sources.each_source do |source|
+ begin
+ names = case type
+ when :latest
+ tuples_for source, :latest
+ when :released
+ tuples_for source, :released
+ when :complete
+ names =
+ tuples_for(source, :prerelease, true) +
+ tuples_for(source, :released)
+
+ names.sort
+ when :abs_latest
+ names =
+ tuples_for(source, :prerelease, true) +
+ tuples_for(source, :latest)
+
+ names.sort
+ when :prerelease
+ tuples_for(source, :prerelease)
+ else
+ raise Gem::Exception, "Unknown type - :#{type}"
+ end
+ rescue Gem::RemoteFetcher::FetchError => e
+ errors << Gem::SourceFetchProblem.new(source, e)
+ else
+ list[source] = names
+ end
+ end
+
+ [list, errors]
+ end
+
+ ##
+ # Retrieves NameTuples from +source+ of the given +type+ (:prerelease,
+ # etc.). If +gracefully_ignore+ is true, errors are ignored.
+
+ def tuples_for(source, type, gracefully_ignore=false) # :nodoc:
+ @caches[type][source.uri] ||=
+ source.load_specs(type).sort_by { |tup| tup.name }
+ rescue Gem::RemoteFetcher::FetchError
+ raise unless gracefully_ignore
+ []
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/specification.rb b/jni/ruby/lib/rubygems/specification.rb
new file mode 100644
index 0000000..ab1cd92
--- /dev/null
+++ b/jni/ruby/lib/rubygems/specification.rb
@@ -0,0 +1,2800 @@
+# -*- coding: utf-8 -*-
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+
+require 'rubygems/version'
+require 'rubygems/requirement'
+require 'rubygems/platform'
+require 'rubygems/deprecate'
+require 'rubygems/basic_specification'
+require 'rubygems/stub_specification'
+require 'rubygems/util/stringio'
+
+##
+# The Specification class contains the information for a Gem. Typically
+# defined in a .gemspec file or a Rakefile, and looks like this:
+#
+# Gem::Specification.new do |s|
+# s.name = 'example'
+# s.version = '0.1.0'
+# s.licenses = ['MIT']
+# s.summary = "This is an example!"
+# s.description = "Much longer explanation of the example!"
+# s.authors = ["Ruby Coder"]
+# s.email = 'rubycoder@example.com'
+# s.files = ["lib/example.rb"]
+# s.homepage = 'https://rubygems.org/gems/example'
+# end
+#
+# Starting in RubyGems 2.0, a Specification can hold arbitrary
+# metadata. See #metadata for restrictions on the format and size of metadata
+# items you may add to a specification.
+
+class Gem::Specification < Gem::BasicSpecification
+
+ # REFACTOR: Consider breaking out this version stuff into a separate
+ # module. There's enough special stuff around it that it may justify
+ # a separate class.
+
+ ##
+ # The version number of a specification that does not specify one
+ # (i.e. RubyGems 0.7 or earlier).
+
+ NONEXISTENT_SPECIFICATION_VERSION = -1
+
+ ##
+ # The specification version applied to any new Specification instances
+ # created. This should be bumped whenever something in the spec format
+ # changes.
+ #
+ # Specification Version History:
+ #
+ # spec ruby
+ # ver ver yyyy-mm-dd description
+ # -1 <0.8.0 pre-spec-version-history
+ # 1 0.8.0 2004-08-01 Deprecated "test_suite_file" for "test_files"
+ # "test_file=x" is a shortcut for "test_files=[x]"
+ # 2 0.9.5 2007-10-01 Added "required_rubygems_version"
+ # Now forward-compatible with future versions
+ # 3 1.3.2 2009-01-03 Added Fixnum validation to specification_version
+ # 4 1.9.0 2011-06-07 Added metadata
+ #--
+ # When updating this number, be sure to also update #to_ruby.
+ #
+ # NOTE RubyGems < 1.2 cannot load specification versions > 2.
+
+ CURRENT_SPECIFICATION_VERSION = 4 # :nodoc:
+
+ ##
+ # An informal list of changes to the specification. The highest-valued
+ # key should be equal to the CURRENT_SPECIFICATION_VERSION.
+
+ SPECIFICATION_VERSION_HISTORY = { # :nodoc:
+ -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
+ 1 => [
+ 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"',
+ '"test_file=x" is a shortcut for "test_files=[x]"'
+ ],
+ 2 => [
+ 'Added "required_rubygems_version"',
+ 'Now forward-compatible with future versions',
+ ],
+ 3 => [
+ 'Added Fixnum validation to the specification_version'
+ ],
+ 4 => [
+ 'Added sandboxed freeform metadata to the specification version.'
+ ]
+ }
+
+ MARSHAL_FIELDS = { # :nodoc:
+ -1 => 16,
+ 1 => 16,
+ 2 => 16,
+ 3 => 17,
+ 4 => 18,
+ }
+
+ today = Time.now.utc
+ TODAY = Time.utc(today.year, today.month, today.day) # :nodoc:
+
+ LOAD_CACHE = {} # :nodoc:
+
+ private_constant :LOAD_CACHE if defined? private_constant
+
+ # :startdoc:
+
+ ##
+ # List of attribute names: [:name, :version, ...]
+
+ @@required_attributes = [:rubygems_version,
+ :specification_version,
+ :name,
+ :version,
+ :date,
+ :summary,
+ :require_paths]
+
+ ##
+ # Map of attribute names to default values.
+
+ @@default_value = {
+ :authors => [],
+ :autorequire => nil,
+ :bindir => 'bin',
+ :cert_chain => [],
+ :date => TODAY,
+ :dependencies => [],
+ :description => nil,
+ :email => nil,
+ :executables => [],
+ :extensions => [],
+ :extra_rdoc_files => [],
+ :files => [],
+ :homepage => nil,
+ :licenses => [],
+ :metadata => {},
+ :name => nil,
+ :platform => Gem::Platform::RUBY,
+ :post_install_message => nil,
+ :rdoc_options => [],
+ :require_paths => ['lib'],
+ :required_ruby_version => Gem::Requirement.default,
+ :required_rubygems_version => Gem::Requirement.default,
+ :requirements => [],
+ :rubyforge_project => nil,
+ :rubygems_version => Gem::VERSION,
+ :signing_key => nil,
+ :specification_version => CURRENT_SPECIFICATION_VERSION,
+ :summary => nil,
+ :test_files => [],
+ :version => nil,
+ }
+
+ Dupable = { } # :nodoc:
+
+ @@default_value.each do |k,v|
+ case v
+ when Time, Numeric, Symbol, true, false, nil
+ Dupable[k] = false
+ else
+ Dupable[k] = true
+ end
+ end
+
+ @@attributes = @@default_value.keys.sort_by { |s| s.to_s }
+ @@array_attributes = @@default_value.reject { |k,v| v != [] }.keys
+ @@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k|
+ @@default_value[k].nil?
+ }
+
+ ######################################################################
+ # :section: Required gemspec attributes
+
+ ##
+ # This gem's name.
+ #
+ # Usage:
+ #
+ # spec.name = 'rake'
+
+ attr_accessor :name
+
+ ##
+ # This gem's version.
+ #
+ # The version string can contain numbers and periods, such as +1.0.0+.
+ # A gem is a 'prerelease' gem if the version has a letter in it, such as
+ # +1.0.0.pre+.
+ #
+ # Usage:
+ #
+ # spec.version = '0.4.1'
+
+ attr_reader :version
+
+ ##
+ # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
+ # activated.
+ #
+ # See also #require_paths
+ #
+ # If you have an extension you do not need to add <code>"ext"</code> to the
+ # require path, the extension build process will copy the extension files
+ # into "lib" for you.
+ #
+ # The default value is <code>"lib"</code>
+ #
+ # Usage:
+ #
+ # # If all library files are in the root directory...
+ # spec.require_paths = ['.']
+
+ def require_paths=(val)
+ @require_paths = Array(val)
+ end
+
+ ##
+ # The version of RubyGems used to create this gem.
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
+
+ attr_accessor :rubygems_version
+
+ ##
+ # A short summary of this gem's description. Displayed in `gem list -d`.
+ #
+ # The #description should be more detailed than the summary.
+ #
+ # Usage:
+ #
+ # spec.summary = "This is a small summary of my gem"
+
+ attr_reader :summary
+
+ ##
+ # Singular writer for #authors
+ #
+ # Usage:
+ #
+ # spec.author = 'John Jones'
+
+ def author= o
+ self.authors = [o]
+ end
+
+ ##
+ # Sets the list of authors, ensuring it is an array.
+ #
+ # Usage:
+ #
+ # spec.authors = ['John Jones', 'Mary Smith']
+
+ def authors= value
+ @authors = Array(value).flatten.grep(String)
+ end
+
+ ##
+ # The platform this gem runs on.
+ #
+ # This is usually Gem::Platform::RUBY or Gem::Platform::CURRENT.
+ #
+ # Most gems contain pure Ruby code; they should simply leave the default
+ # value in place. Some gems contain C (or other) code to be compiled into a
+ # Ruby "extension". The gem should leave the default value in place unless
+ # the code will only compile on a certain type of system. Some gems consist
+ # of pre-compiled code ("binary gems"). It's especially important that they
+ # set the platform attribute appropriately. A shortcut is to set the
+ # platform to Gem::Platform::CURRENT, which will cause the gem builder to set
+ # the platform to the appropriate value for the system on which the build is
+ # being performed.
+ #
+ # If this attribute is set to a non-default value, it will be included in
+ # the filename of the gem when it is built such as:
+ # nokogiri-1.6.0-x86-mingw32.gem
+ #
+ # Usage:
+ #
+ # spec.platform = Gem::Platform.local
+
+ def platform= platform
+ if @original_platform.nil? or
+ @original_platform == Gem::Platform::RUBY then
+ @original_platform = platform
+ end
+
+ case platform
+ when Gem::Platform::CURRENT then
+ @new_platform = Gem::Platform.local
+ @original_platform = @new_platform.to_s
+
+ when Gem::Platform then
+ @new_platform = platform
+
+ # legacy constants
+ when nil, Gem::Platform::RUBY then
+ @new_platform = Gem::Platform::RUBY
+ when 'mswin32' then # was Gem::Platform::WIN32
+ @new_platform = Gem::Platform.new 'x86-mswin32'
+ when 'i586-linux' then # was Gem::Platform::LINUX_586
+ @new_platform = Gem::Platform.new 'x86-linux'
+ when 'powerpc-darwin' then # was Gem::Platform::DARWIN
+ @new_platform = Gem::Platform.new 'ppc-darwin'
+ else
+ @new_platform = Gem::Platform.new platform
+ end
+
+ @platform = @new_platform.to_s
+
+ invalidate_memoized_attributes
+
+ @new_platform
+ end
+
+ ##
+ # Files included in this gem. You cannot append to this accessor, you must
+ # assign to it.
+ #
+ # Only add files you can require to this list, not directories, etc.
+ #
+ # Directories are automatically stripped from this list when building a gem,
+ # other non-files cause an error.
+ #
+ # Usage:
+ #
+ # require 'rake'
+ # spec.files = FileList['lib/**/*.rb',
+ # 'bin/*',
+ # '[A-Z]*',
+ # 'test/**/*'].to_a
+ #
+ # # or without Rake...
+ # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*']
+ # spec.files += Dir['[A-Z]*'] + Dir['test/**/*']
+ # spec.files.reject! { |fn| fn.include? "CVS" }
+
+ def files
+ # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
+ # DOC: Why isn't it normal? Why does it suck? How can we fix this?
+ @files = [@files,
+ @test_files,
+ add_bindir(@executables),
+ @extra_rdoc_files,
+ @extensions,
+ ].flatten.uniq.compact.sort
+ end
+
+ ######################################################################
+ # :section: Optional gemspec attributes
+
+ ##
+ # The path in the gem for executable scripts. Usually 'bin'
+ #
+ # Usage:
+ #
+ # spec.bindir = 'bin'
+
+ attr_accessor :bindir
+
+ ##
+ # The certificate chain used to sign this gem. See Gem::Security for
+ # details.
+
+ attr_accessor :cert_chain
+
+ ##
+ # A long description of this gem
+ #
+ # The description should be more detailed than the summary but not
+ # excessively long. A few paragraphs is a recommended length with no
+ # examples or formatting.
+ #
+ # Usage:
+ #
+ # spec.description = <<-EOF
+ # Rake is a Make-like program implemented in Ruby. Tasks and
+ # dependencies are specified in standard Ruby syntax.
+ # EOF
+
+ attr_reader :description
+
+ ##
+ # A contact email address (or addresses) for this gem
+ #
+ # Usage:
+ #
+ # spec.email = 'john.jones@example.com'
+ # spec.email = ['jack@example.com', 'jill@example.com']
+
+ attr_accessor :email
+
+ ##
+ # The URL of this gem's home page
+ #
+ # Usage:
+ #
+ # spec.homepage = 'http://rake.rubyforge.org'
+
+ attr_accessor :homepage
+
+ ##
+ # A message that gets displayed after the gem is installed.
+ #
+ # Usage:
+ #
+ # spec.post_install_message = "Thanks for installing!"
+
+ attr_accessor :post_install_message
+
+ ##
+ # The version of Ruby required by this gem
+
+ attr_reader :required_ruby_version
+
+ ##
+ # The RubyGems version required by this gem
+
+ attr_reader :required_rubygems_version
+
+ ##
+ # The key used to sign this gem. See Gem::Security for details.
+
+ attr_accessor :signing_key
+
+ ##
+ # :attr_accessor: metadata
+ #
+ # The metadata holds extra data for this gem that may be useful to other
+ # consumers and is settable by gem authors without requiring an update to
+ # the rubygems software.
+ #
+ # Metadata items have the following restrictions:
+ #
+ # * The metadata must be a Hash object
+ # * All keys and values must be Strings
+ # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024
+ # bytes
+ # * All strings must be UTF-8, no binary data is allowed
+ #
+ # To add metadata for the location of a issue tracker:
+ #
+ # s.metadata = { "issue_tracker" => "https://example/issues" }
+
+ attr_accessor :metadata
+
+ ##
+ # Adds a development dependency named +gem+ with +requirements+ to this
+ # gem.
+ #
+ # Usage:
+ #
+ # spec.add_development_dependency 'example', '~> 1.1', '>= 1.1.4'
+ #
+ # Development dependencies aren't installed by default and aren't
+ # activated when a gem is required.
+
+ def add_development_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :development, *requirements)
+ end
+
+ ##
+ # Adds a runtime dependency named +gem+ with +requirements+ to this gem.
+ #
+ # Usage:
+ #
+ # spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4'
+
+ def add_runtime_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :runtime, *requirements)
+ end
+
+ ##
+ # Executables included in the gem.
+ #
+ # For example, the rake gem has rake as an executable. You don’t specify the
+ # full path (as in bin/rake); all application-style files are expected to be
+ # found in bindir. These files must be executable Ruby files. Files that
+ # use bash or other interpreters will not work.
+ #
+ # Executables included may only be ruby scripts, not scripts for other
+ # languages or compiled binaries.
+ #
+ # Usage:
+ #
+ # spec.executables << 'rake'
+
+ def executables
+ @executables ||= []
+ end
+
+ ##
+ # Extensions to build when installing the gem, specifically the paths to
+ # extconf.rb-style files used to compile extensions.
+ #
+ # These files will be run when the gem is installed, causing the C (or
+ # whatever) code to be compiled on the user’s machine.
+ #
+ # Usage:
+ #
+ # spec.extensions << 'ext/rmagic/extconf.rb'
+ #
+ # See Gem::Ext::Builder for information about writing extensions for gems.
+
+ def extensions
+ @extensions ||= []
+ end
+
+ ##
+ # Extra files to add to RDoc such as README or doc/examples.txt
+ #
+ # When the user elects to generate the RDoc documentation for a gem (typically
+ # at install time), all the library files are sent to RDoc for processing.
+ # This option allows you to have some non-code files included for a more
+ # complete set of documentation.
+ #
+ # Usage:
+ #
+ # spec.extra_rdoc_files = ['README', 'doc/user-guide.txt']
+
+ def extra_rdoc_files
+ @extra_rdoc_files ||= []
+ end
+
+ ##
+ # The version of RubyGems that installed this gem. Returns
+ # <code>Gem::Version.new(0)</code> for gems installed by versions earlier
+ # than RubyGems 2.2.0.
+
+ def installed_by_version # :nodoc:
+ @installed_by_version ||= Gem::Version.new(0)
+ end
+
+ ##
+ # Sets the version of RubyGems that installed this gem. See also
+ # #installed_by_version.
+
+ def installed_by_version= version # :nodoc:
+ @installed_by_version = Gem::Version.new version
+ end
+
+ ##
+ # :category: Recommended gemspec attributes
+ #
+ # The license for this gem.
+ #
+ # The license must be no more than 64 characters.
+ #
+ # This should just be the name of your license. The full text of the license
+ # should be inside of the gem (at the top level) when you build it.
+ #
+ # The simplest way, is to specify the standard SPDX ID
+ # https://spdx.org/licenses/ for the license.
+ # Ideally you should pick one that is OSI (Open Source Initiative)
+ # http://opensource.org/licenses/alphabetical approved.
+ #
+ # The most commonly used OSI approved licenses are BSD-3-Clause and MIT.
+ # GitHub also provides a license picker at http://choosealicense.com/.
+ #
+ # You should specify a license for your gem so that people know how they are
+ # permitted to use it, and any restrictions you're placing on it. Not
+ # specifying a license means all rights are reserved; others have no rights
+ # to use the code for any purpose.
+ #
+ # You can set multiple licenses with #licenses=
+ #
+ # Usage:
+ # spec.license = 'MIT'
+
+ def license=o
+ self.licenses = [o]
+ end
+
+ ##
+ # :category: Recommended gemspec attributes
+ # The license(s) for the library.
+ #
+ # Each license must be a short name, no more than 64 characters.
+ #
+ # This should just be the name of your license. The full
+ # text of the license should be inside of the gem when you build it.
+ #
+ # See #license= for more discussion
+ #
+ # Usage:
+ # spec.licenses = ['MIT', 'GPL-2']
+
+ def licenses= licenses
+ @licenses = Array licenses
+ end
+
+ ##
+ # Specifies the rdoc options to be used when generating API documentation.
+ #
+ # Usage:
+ #
+ # spec.rdoc_options << '--title' << 'Rake -- Ruby Make' <<
+ # '--main' << 'README' <<
+ # '--line-numbers'
+
+ def rdoc_options
+ @rdoc_options ||= []
+ end
+
+ ##
+ # The version of Ruby required by this gem. The ruby version can be
+ # specified to the patch-level:
+ #
+ # $ ruby -v -e 'p Gem.ruby_version'
+ # ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0]
+ # #<Gem::Version "2.0.0.247">
+ #
+ # Usage:
+ #
+ # # This gem will work with 1.8.6 or greater...
+ # spec.required_ruby_version = '>= 1.8.6'
+ #
+ # # Only with ruby 2.0.x
+ # spec.required_ruby_version = '~> 2.0'
+
+ def required_ruby_version= req
+ @required_ruby_version = Gem::Requirement.create req
+ end
+
+ ##
+ # The RubyGems version required by this gem
+
+ def required_rubygems_version= req
+ @required_rubygems_version = Gem::Requirement.create req
+ end
+
+ ##
+ # Lists the external (to RubyGems) requirements that must be met for this gem
+ # to work. It's simply information for the user.
+ #
+ # Usage:
+ #
+ # spec.requirements << 'libmagick, v6.0'
+ # spec.requirements << 'A good graphics card'
+
+ def requirements
+ @requirements ||= []
+ end
+
+ ##
+ # A collection of unit test files. They will be loaded as unit tests when
+ # the user requests a gem to be unit tested.
+ #
+ # Usage:
+ # spec.test_files = Dir.glob('test/tc_*.rb')
+ # spec.test_files = ['tests/test-suite.rb']
+
+ def test_files= files # :nodoc:
+ @test_files = Array files
+ end
+
+ ######################################################################
+ # :section: Specification internals
+
+ ##
+ # True when this gemspec has been activated. This attribute is not persisted.
+
+ attr_accessor :activated
+
+ alias :activated? :activated
+
+ ##
+ # Autorequire was used by old RubyGems to automatically require a file.
+ #
+ # Deprecated: It is neither supported nor functional.
+
+ attr_accessor :autorequire # :nodoc:
+
+ ##
+ # Sets the default executable for this gem.
+ #
+ # Deprecated: You must now specify the executable name to Gem.bin_path.
+
+ attr_writer :default_executable
+
+ ##
+ # Allows deinstallation of gems with legacy platforms.
+
+ attr_writer :original_platform # :nodoc:
+
+ ##
+ # The rubyforge project this gem lives under. i.e. RubyGems'
+ # rubyforge_project is "rubygems".
+ #
+ # This option is deprecated.
+
+ attr_accessor :rubyforge_project
+
+ ##
+ # The Gem::Specification version of this gemspec.
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
+
+ attr_accessor :specification_version
+
+ def self._all # :nodoc:
+ unless defined?(@@all) && @@all then
+ @@all = stubs.map(&:to_spec)
+
+ # After a reset, make sure already loaded specs
+ # are still marked as activated.
+ specs = {}
+ Gem.loaded_specs.each_value{|s| specs[s] = true}
+ @@all.each{|s| s.activated = true if specs[s]}
+ end
+ @@all
+ end
+
+ def self._clear_load_cache # :nodoc:
+ LOAD_CACHE.clear
+ end
+
+ def self.each_gemspec(dirs) # :nodoc:
+ dirs.each do |dir|
+ Dir[File.join(dir, "*.gemspec")].each do |path|
+ yield path.untaint
+ end
+ end
+ end
+
+ def self.each_stub(dirs) # :nodoc:
+ each_gemspec(dirs) do |path|
+ stub = Gem::StubSpecification.new(path)
+ yield stub if stub.valid?
+ end
+ end
+
+ def self.each_spec(dirs) # :nodoc:
+ each_gemspec(dirs) do |path|
+ spec = self.load path
+ yield spec if spec
+ end
+ end
+
+ ##
+ # Returns a Gem::StubSpecification for every installed gem
+
+ def self.stubs
+ @@stubs ||= begin
+ stubs = {}
+ each_stub([default_specifications_dir] + dirs) do |stub|
+ stubs[stub.full_name] ||= stub
+ end
+
+ stubs = stubs.values
+ _resort!(stubs)
+ stubs
+ end
+ end
+
+ def self._resort!(specs) # :nodoc:
+ specs.sort! { |a, b|
+ names = a.name <=> b.name
+ next names if names.nonzero?
+ b.version <=> a.version
+ }
+ end
+
+ ##
+ # Loads the default specifications. It should be called only once.
+
+ def self.load_defaults
+ each_spec([default_specifications_dir]) do |spec|
+ # #load returns nil if the spec is bad, so we just ignore
+ # it at this stage
+ Gem.register_default_spec(spec)
+ end
+ end
+
+ ##
+ # Adds +spec+ to the known specifications, keeping the collection
+ # properly sorted.
+
+ def self.add_spec spec
+ # TODO: find all extraneous adds
+ # puts
+ # p :add_spec => [spec.full_name, caller.reject { |s| s =~ /minitest/ }]
+
+ # TODO: flush the rest of the crap from the tests
+ # raise "no dupes #{spec.full_name} in #{all_names.inspect}" if
+ # _all.include? spec
+
+ raise "nil spec!" unless spec # TODO: remove once we're happy with tests
+
+ return if _all.include? spec
+
+ _all << spec
+ stubs << spec
+ _resort!(_all)
+ _resort!(stubs)
+ end
+
+ ##
+ # Adds multiple specs to the known specifications.
+
+ def self.add_specs *specs
+ raise "nil spec!" if specs.any?(&:nil?) # TODO: remove once we're happy
+
+ # TODO: this is much more efficient, but we need the extra checks for now
+ # _all.concat specs
+ # _resort!
+
+ specs.each do |spec| # TODO: slow
+ add_spec spec
+ end
+ end
+
+ ##
+ # Returns all specifications. This method is discouraged from use.
+ # You probably want to use one of the Enumerable methods instead.
+
+ def self.all
+ warn "NOTE: Specification.all called from #{caller.first}" unless
+ Gem::Deprecate.skip
+ _all
+ end
+
+ ##
+ # Sets the known specs to +specs+. Not guaranteed to work for you in
+ # the future. Use at your own risk. Caveat emptor. Doomy doom doom.
+ # Etc etc.
+ #
+ #--
+ # Makes +specs+ the known specs
+ # Listen, time is a river
+ # Winter comes, code breaks
+ #
+ # -- wilsonb
+
+ def self.all= specs
+ @@all = @@stubs = specs
+ end
+
+ ##
+ # Return full names of all specs in sorted order.
+
+ def self.all_names
+ self._all.map(&:full_name)
+ end
+
+ ##
+ # Return the list of all array-oriented instance variables.
+ #--
+ # Not sure why we need to use so much stupid reflection in here...
+
+ def self.array_attributes
+ @@array_attributes.dup
+ end
+
+ ##
+ # Return the list of all instance variables.
+ #--
+ # Not sure why we need to use so much stupid reflection in here...
+
+ def self.attribute_names
+ @@attributes.dup
+ end
+
+ ##
+ # Return the directories that Specification uses to find specs.
+
+ def self.dirs
+ @@dirs ||= Gem.path.collect { |dir|
+ File.join dir.dup.untaint, "specifications"
+ }
+ end
+
+ ##
+ # Set the directories that Specification uses to find specs. Setting
+ # this resets the list of known specs.
+
+ def self.dirs= dirs
+ self.reset
+
+ @@dirs = Array(dirs).map { |dir| File.join dir, "specifications" }
+ end
+
+ extend Enumerable
+
+ ##
+ # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of
+ # specs.
+
+ def self.each
+ return enum_for(:each) unless block_given?
+
+ self._all.each do |x|
+ yield x
+ end
+ end
+
+ ##
+ # Returns every spec that matches +name+ and optional +requirements+.
+
+ def self.find_all_by_name name, *requirements
+ requirements = Gem::Requirement.default if requirements.empty?
+
+ # TODO: maybe try: find_all { |s| spec === dep }
+
+ Gem::Dependency.new(name, *requirements).matching_specs
+ end
+
+ ##
+ # Find the best specification matching a +name+ and +requirements+. Raises
+ # if the dependency doesn't resolve to a valid specification.
+
+ def self.find_by_name name, *requirements
+ requirements = Gem::Requirement.default if requirements.empty?
+
+ # TODO: maybe try: find { |s| spec === dep }
+
+ Gem::Dependency.new(name, *requirements).to_spec
+ end
+
+ ##
+ # Return the best specification that contains the file matching +path+.
+
+ def self.find_by_path path
+ self.find { |spec|
+ spec.contains_requirable_file? path
+ }
+ end
+
+ ##
+ # Return the best specification that contains the file matching +path+
+ # amongst the specs that are not activated.
+
+ def self.find_inactive_by_path path
+ stub = stubs.find { |s|
+ s.contains_requirable_file? path unless s.activated?
+ }
+ stub && stub.to_spec
+ end
+
+ ##
+ # Return currently unresolved specs that contain the file matching +path+.
+
+ def self.find_in_unresolved path
+ # TODO: do we need these?? Kill it
+ specs = unresolved_deps.values.map { |dep| dep.to_specs }.flatten
+
+ specs.find_all { |spec| spec.contains_requirable_file? path }
+ end
+
+ ##
+ # Search through all unresolved deps and sub-dependencies and return
+ # specs that contain the file matching +path+.
+
+ def self.find_in_unresolved_tree path
+ specs = unresolved_deps.values.map { |dep| dep.to_specs }.flatten
+
+ specs.reverse_each do |spec|
+ trails = []
+ spec.traverse do |from_spec, dep, to_spec, trail|
+ next unless to_spec.conflicts.empty?
+ trails << trail if to_spec.contains_requirable_file? path
+ end
+
+ next if trails.empty?
+
+ return trails.map(&:reverse).sort.first.reverse
+ end
+
+ []
+ end
+
+ ##
+ # Special loader for YAML files. When a Specification object is loaded
+ # from a YAML file, it bypasses the normal Ruby object initialization
+ # routine (#initialize). This method makes up for that and deals with
+ # gems of different ages.
+ #
+ # +input+ can be anything that YAML.load() accepts: String or IO.
+
+ def self.from_yaml(input)
+ Gem.load_yaml
+
+ input = normalize_yaml_input input
+ spec = YAML.load input
+
+ if spec && spec.class == FalseClass then
+ raise Gem::EndOfYAMLException
+ end
+
+ unless Gem::Specification === spec then
+ raise Gem::Exception, "YAML data doesn't evaluate to gem specification"
+ end
+
+ spec.specification_version ||= NONEXISTENT_SPECIFICATION_VERSION
+ spec.reset_nil_attributes_to_default
+
+ spec
+ end
+
+ ##
+ # Return the latest specs, optionally including prerelease specs if
+ # +prerelease+ is true.
+
+ def self.latest_specs prerelease = false
+ result = Hash.new { |h,k| h[k] = {} }
+ native = {}
+
+ Gem::Specification.reverse_each do |spec|
+ next if spec.version.prerelease? unless prerelease
+
+ native[spec.name] = spec.version if spec.platform == Gem::Platform::RUBY
+ result[spec.name][spec.platform] = spec
+ end
+
+ result.map(&:last).map(&:values).flatten.reject { |spec|
+ minimum = native[spec.name]
+ minimum && spec.version < minimum
+ }.sort_by{ |tup| tup.name }
+ end
+
+ ##
+ # Loads Ruby format gemspec from +file+.
+
+ def self.load file
+ return unless file
+ file = file.dup.untaint
+ return unless File.file?(file)
+
+ spec = LOAD_CACHE[file]
+ return spec if spec
+
+ code = if defined? Encoding
+ File.read file, :mode => 'r:UTF-8:-'
+ else
+ File.read file
+ end
+
+ code.untaint
+
+ begin
+ spec = eval code, binding, file
+
+ if Gem::Specification === spec
+ spec.loaded_from = File.expand_path file.to_s
+ LOAD_CACHE[file] = spec
+ return spec
+ end
+
+ warn "[#{file}] isn't a Gem::Specification (#{spec.class} instead)."
+ rescue SignalException, SystemExit
+ raise
+ rescue SyntaxError, Exception => e
+ warn "Invalid gemspec in [#{file}]: #{e}"
+ end
+
+ nil
+ end
+
+ ##
+ # Specification attributes that must be non-nil
+
+ def self.non_nil_attributes
+ @@non_nil_attributes.dup
+ end
+
+ ##
+ # Make sure the YAML specification is properly formatted with dashes
+
+ def self.normalize_yaml_input(input)
+ result = input.respond_to?(:read) ? input.read : input
+ result = "--- " + result unless result =~ /\A--- /
+ result.gsub!(/ !!null \n/, " \n")
+ # date: 2011-04-26 00:00:00.000000000Z
+ # date: 2011-04-26 00:00:00.000000000 Z
+ result.gsub!(/^(date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+?)Z/, '\1 Z')
+ result
+ end
+
+ ##
+ # Return a list of all outdated local gem names. This method is HEAVY
+ # as it must go fetch specifications from the server.
+ #
+ # Use outdated_and_latest_version if you wish to retrieve the latest remote
+ # version as well.
+
+ def self.outdated
+ outdated_and_latest_version.map { |local, _| local.name }
+ end
+
+ ##
+ # Enumerates the outdated local gems yielding the local specification and
+ # the latest remote version.
+ #
+ # This method may take some time to return as it must check each local gem
+ # against the server's index.
+
+ def self.outdated_and_latest_version
+ return enum_for __method__ unless block_given?
+
+ # TODO: maybe we should switch to rubygems' version service?
+ fetcher = Gem::SpecFetcher.fetcher
+
+ latest_specs(true).each do |local_spec|
+ dependency =
+ Gem::Dependency.new local_spec.name, ">= #{local_spec.version}"
+
+ remotes, = fetcher.search_for_dependency dependency
+ remotes = remotes.map { |n, _| n.version }
+
+ latest_remote = remotes.sort.last
+
+ yield [local_spec, latest_remote] if
+ latest_remote and local_spec.version < latest_remote
+ end
+
+ nil
+ end
+
+ ##
+ # Removes +spec+ from the known specs.
+
+ def self.remove_spec spec
+ _all.delete spec
+ stubs.delete_if { |s| s.full_name == spec.full_name }
+ end
+
+ ##
+ # Is +name+ a required attribute?
+
+ def self.required_attribute?(name)
+ @@required_attributes.include? name.to_sym
+ end
+
+ ##
+ # Required specification attributes
+
+ def self.required_attributes
+ @@required_attributes.dup
+ end
+
+ ##
+ # Reset the list of known specs, running pre and post reset hooks
+ # registered in Gem.
+
+ def self.reset
+ @@dirs = nil
+ Gem.pre_reset_hooks.each { |hook| hook.call }
+ @@all = nil
+ @@stubs = nil
+ _clear_load_cache
+ unresolved = unresolved_deps
+ unless unresolved.empty? then
+ w = "W" + "ARN"
+ warn "#{w}: Unresolved specs during Gem::Specification.reset:"
+ unresolved.values.each do |dep|
+ warn " #{dep}"
+ end
+ warn "#{w}: Clearing out unresolved specs."
+ warn "Please report a bug if this causes problems."
+ unresolved.clear
+ end
+ Gem.post_reset_hooks.each { |hook| hook.call }
+ end
+
+ # DOC: This method needs documented or nodoc'd
+ def self.unresolved_deps
+ @unresolved_deps ||= Hash.new { |h, n| h[n] = Gem::Dependency.new n }
+ end
+
+ ##
+ # Load custom marshal format, re-initializing defaults as needed
+
+ def self._load(str)
+ array = Marshal.load str
+
+ spec = Gem::Specification.new
+ spec.instance_variable_set :@specification_version, array[1]
+
+ current_version = CURRENT_SPECIFICATION_VERSION
+
+ field_count = if spec.specification_version > current_version then
+ spec.instance_variable_set :@specification_version,
+ current_version
+ MARSHAL_FIELDS[current_version]
+ else
+ MARSHAL_FIELDS[spec.specification_version]
+ end
+
+ if array.size < field_count then
+ raise TypeError, "invalid Gem::Specification format #{array.inspect}"
+ end
+
+ # Cleanup any YAML::PrivateType. They only show up for an old bug
+ # where nil => null, so just convert them to nil based on the type.
+
+ array.map! { |e| e.kind_of?(YAML::PrivateType) ? nil : e }
+
+ spec.instance_variable_set :@rubygems_version, array[0]
+ # spec version
+ spec.instance_variable_set :@name, array[2]
+ spec.instance_variable_set :@version, array[3]
+ spec.date = array[4]
+ spec.instance_variable_set :@summary, array[5]
+ spec.instance_variable_set :@required_ruby_version, array[6]
+ spec.instance_variable_set :@required_rubygems_version, array[7]
+ spec.instance_variable_set :@original_platform, array[8]
+ spec.instance_variable_set :@dependencies, array[9]
+ spec.instance_variable_set :@rubyforge_project, array[10]
+ spec.instance_variable_set :@email, array[11]
+ spec.instance_variable_set :@authors, array[12]
+ spec.instance_variable_set :@description, array[13]
+ spec.instance_variable_set :@homepage, array[14]
+ spec.instance_variable_set :@has_rdoc, array[15]
+ spec.instance_variable_set :@new_platform, array[16]
+ spec.instance_variable_set :@platform, array[16].to_s
+ spec.instance_variable_set :@license, array[17]
+ spec.instance_variable_set :@metadata, array[18]
+ spec.instance_variable_set :@loaded, false
+ spec.instance_variable_set :@activated, false
+
+ spec
+ end
+
+ def <=>(other) # :nodoc:
+ sort_obj <=> other.sort_obj
+ end
+
+ def == other # :nodoc:
+ self.class === other &&
+ name == other.name &&
+ version == other.version &&
+ platform == other.platform
+ end
+
+ ##
+ # Dump only crucial instance variables.
+ #--
+ # MAINTAIN ORDER!
+ # (down with the man)
+
+ def _dump(limit)
+ Marshal.dump [
+ @rubygems_version,
+ @specification_version,
+ @name,
+ @version,
+ date,
+ @summary,
+ @required_ruby_version,
+ @required_rubygems_version,
+ @original_platform,
+ @dependencies,
+ @rubyforge_project,
+ @email,
+ @authors,
+ @description,
+ @homepage,
+ true, # has_rdoc
+ @new_platform,
+ @licenses,
+ @metadata
+ ]
+ end
+
+ ##
+ # Activate this spec, registering it as a loaded spec and adding
+ # it's lib paths to $LOAD_PATH. Returns true if the spec was
+ # activated, false if it was previously activated. Freaks out if
+ # there are conflicts upon activation.
+
+ def activate
+ other = Gem.loaded_specs[self.name]
+ if other then
+ check_version_conflict other
+ return false
+ end
+
+ raise_if_conflicts
+
+ activate_dependencies
+ add_self_to_load_path
+
+ Gem.loaded_specs[self.name] = self
+ @activated = true
+ @loaded = true
+
+ return true
+ end
+
+ ##
+ # Activate all unambiguously resolved runtime dependencies of this
+ # spec. Add any ambiguous dependencies to the unresolved list to be
+ # resolved later, as needed.
+
+ def activate_dependencies
+ unresolved = Gem::Specification.unresolved_deps
+
+ self.runtime_dependencies.each do |spec_dep|
+ if loaded = Gem.loaded_specs[spec_dep.name]
+ next if spec_dep.matches_spec? loaded
+
+ msg = "can't satisfy '#{spec_dep}', already activated '#{loaded.full_name}'"
+ e = Gem::LoadError.new msg
+ e.name = spec_dep.name
+
+ raise e
+ end
+
+ specs = spec_dep.to_specs
+
+ if specs.size == 1 then
+ specs.first.activate
+ else
+ name = spec_dep.name
+ unresolved[name] = unresolved[name].merge spec_dep
+ end
+ end
+
+ unresolved.delete self.name
+ end
+
+ ##
+ # Returns an array with bindir attached to each executable in the
+ # +executables+ list
+
+ def add_bindir(executables)
+ return nil if executables.nil?
+
+ if @bindir then
+ Array(executables).map { |e| File.join(@bindir, e) }
+ else
+ executables
+ end
+ rescue
+ return nil
+ end
+
+ ##
+ # Adds a dependency on gem +dependency+ with type +type+ that requires
+ # +requirements+. Valid types are currently <tt>:runtime</tt> and
+ # <tt>:development</tt>.
+
+ def add_dependency_with_type(dependency, type, *requirements)
+ requirements = if requirements.empty? then
+ Gem::Requirement.default
+ else
+ requirements.flatten
+ end
+
+ unless dependency.respond_to?(:name) &&
+ dependency.respond_to?(:version_requirements)
+ dependency = Gem::Dependency.new(dependency.to_s, requirements, type)
+ end
+
+ dependencies << dependency
+ end
+
+ private :add_dependency_with_type
+
+ alias add_dependency add_runtime_dependency
+
+ ##
+ # Adds this spec's require paths to LOAD_PATH, in the proper location.
+
+ def add_self_to_load_path
+ return if default_gem?
+
+ paths = full_require_paths
+
+ # gem directories must come after -I and ENV['RUBYLIB']
+ insert_index = Gem.load_path_insert_index
+
+ if insert_index then
+ # gem directories must come after -I and ENV['RUBYLIB']
+ $LOAD_PATH.insert(insert_index, *paths)
+ else
+ # we are probably testing in core, -I and RUBYLIB don't apply
+ $LOAD_PATH.unshift(*paths)
+ end
+ end
+
+ ##
+ # Singular reader for #authors. Returns the first author in the list
+
+ def author
+ val = authors and val.first
+ end
+
+ ##
+ # The list of author names who wrote this gem.
+ #
+ # spec.authors = ['Chad Fowler', 'Jim Weirich', 'Rich Kilmer']
+
+ def authors
+ @authors ||= []
+ end
+
+ ##
+ # Returns the full path to installed gem's bin directory.
+ #
+ # NOTE: do not confuse this with +bindir+, which is just 'bin', not
+ # a full path.
+
+ def bin_dir
+ @bin_dir ||= File.join gem_dir, bindir # TODO: this is unfortunate
+ end
+
+ ##
+ # Returns the full path to an executable named +name+ in this gem.
+
+ def bin_file name
+ File.join bin_dir, name
+ end
+
+ ##
+ # Returns the build_args used to install the gem
+
+ def build_args
+ if File.exist? build_info_file
+ build_info = File.readlines build_info_file
+ build_info = build_info.map { |x| x.strip }
+ build_info.delete ""
+ build_info
+ else
+ []
+ end
+ end
+
+ ##
+ # Builds extensions for this platform if the gem has extensions listed and
+ # the gem.build_complete file is missing.
+
+ def build_extensions # :nodoc:
+ return if default_gem?
+ return if extensions.empty?
+ return if installed_by_version < Gem::Version.new('2.2.0.preview.2')
+ return if File.exist? gem_build_complete_path
+ return if !File.writable?(base_dir)
+ return if !File.exist?(File.join(base_dir, 'extensions'))
+
+ begin
+ # We need to require things in $LOAD_PATH without looking for the
+ # extension we are about to build.
+ unresolved_deps = Gem::Specification.unresolved_deps.dup
+ Gem::Specification.unresolved_deps.clear
+
+ require 'rubygems/config_file'
+ require 'rubygems/ext'
+ require 'rubygems/user_interaction'
+
+ ui = Gem::SilentUI.new
+ Gem::DefaultUserInteraction.use_ui ui do
+ builder = Gem::Ext::Builder.new self
+ builder.build_extensions
+ end
+ ensure
+ ui.close if ui
+ Gem::Specification.unresolved_deps.replace unresolved_deps
+ end
+ end
+
+ ##
+ # Returns the full path to the build info directory
+
+ def build_info_dir
+ File.join base_dir, "build_info"
+ end
+
+ ##
+ # Returns the full path to the file containing the build
+ # information generated when the gem was installed
+
+ def build_info_file
+ File.join build_info_dir, "#{full_name}.info"
+ end
+
+ ##
+ # Used to detect if the gem is bundled in older version of Ruby, but not
+ # detectable as default gem (see BasicSpecification#default_gem?).
+
+ def bundled_gem_in_old_ruby?
+ !default_gem? &&
+ RUBY_VERSION < "2.0.0" &&
+ summary == "This #{name} is bundled with Ruby"
+ end
+
+ ##
+ # Returns the full path to the cache directory containing this
+ # spec's cached gem.
+
+ def cache_dir
+ @cache_dir ||= File.join base_dir, "cache"
+ end
+
+ ##
+ # Returns the full path to the cached gem for this spec.
+
+ def cache_file
+ @cache_file ||= File.join cache_dir, "#{full_name}.gem"
+ end
+
+ ##
+ # Return any possible conflicts against the currently loaded specs.
+
+ def conflicts
+ conflicts = {}
+ self.runtime_dependencies.each { |dep|
+ spec = Gem.loaded_specs[dep.name]
+ if spec and not spec.satisfies_requirement? dep
+ (conflicts[spec] ||= []) << dep
+ end
+ }
+ conflicts
+ end
+
+ ##
+ # The date this gem was created. Lazily defaults to the current UTC date.
+ #
+ # There is no need to set this in your gem specification.
+
+ def date
+ @date ||= TODAY
+ end
+
+ DateLike = Object.new # :nodoc:
+ def DateLike.===(obj) # :nodoc:
+ defined?(::Date) and Date === obj
+ end
+
+ DateTimeFormat = # :nodoc:
+ /\A
+ (\d{4})-(\d{2})-(\d{2})
+ (\s+ \d{2}:\d{2}:\d{2}\.\d+ \s* (Z | [-+]\d\d:\d\d) )?
+ \Z/x
+
+ ##
+ # The date this gem was created
+ #
+ # DO NOT set this, it is set automatically when the gem is packaged.
+
+ def date= date
+ # We want to end up with a Time object with one-day resolution.
+ # This is the cleanest, most-readable, faster-than-using-Date
+ # way to do it.
+ @date = case date
+ when String then
+ if DateTimeFormat =~ date then
+ Time.utc($1.to_i, $2.to_i, $3.to_i)
+
+ # Workaround for where the date format output from psych isn't
+ # parsed as a Time object by syck and thus comes through as a
+ # string.
+ elsif /\A(\d{4})-(\d{2})-(\d{2}) \d{2}:\d{2}:\d{2}\.\d+?Z\z/ =~ date then
+ Time.utc($1.to_i, $2.to_i, $3.to_i)
+ else
+ raise(Gem::InvalidSpecificationException,
+ "invalid date format in specification: #{date.inspect}")
+ end
+ when Time, DateLike then
+ Time.utc(date.year, date.month, date.day)
+ else
+ TODAY
+ end
+ end
+
+ ##
+ # The default executable for this gem.
+ #
+ # Deprecated: The name of the gem is assumed to be the name of the
+ # executable now. See Gem.bin_path.
+
+ def default_executable # :nodoc:
+ if defined?(@default_executable) and @default_executable
+ result = @default_executable
+ elsif @executables and @executables.size == 1
+ result = Array(@executables).first
+ else
+ result = nil
+ end
+ result
+ end
+
+ ##
+ # The default value for specification attribute +name+
+
+ def default_value name
+ @@default_value[name]
+ end
+
+ ##
+ # A list of Gem::Dependency objects this gem depends on.
+ #
+ # Use #add_dependency or #add_development_dependency to add dependencies to
+ # a gem.
+
+ def dependencies
+ @dependencies ||= []
+ end
+
+ ##
+ # Return a list of all gems that have a dependency on this gemspec. The
+ # list is structured with entries that conform to:
+ #
+ # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]]
+
+ def dependent_gems
+ out = []
+ Gem::Specification.each do |spec|
+ spec.dependencies.each do |dep|
+ if self.satisfies_requirement?(dep) then
+ sats = []
+ find_all_satisfiers(dep) do |sat|
+ sats << sat
+ end
+ out << [spec, dep, sats]
+ end
+ end
+ end
+ out
+ end
+
+ ##
+ # Returns all specs that matches this spec's runtime dependencies.
+
+ def dependent_specs
+ runtime_dependencies.map { |dep| dep.to_specs }.flatten
+ end
+
+ ##
+ # A detailed description of this gem. See also #summary
+
+ def description= str
+ @description = str.to_s
+ end
+
+ ##
+ # List of dependencies that are used for development
+
+ def development_dependencies
+ dependencies.select { |d| d.type == :development }
+ end
+
+ ##
+ # Returns the full path to this spec's documentation directory. If +type+
+ # is given it will be appended to the end. For example:
+ #
+ # spec.doc_dir # => "/path/to/gem_repo/doc/a-1"
+ #
+ # spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri"
+
+ def doc_dir type = nil
+ @doc_dir ||= File.join base_dir, 'doc', full_name
+
+ if type then
+ File.join @doc_dir, type
+ else
+ @doc_dir
+ end
+ end
+
+ def encode_with coder # :nodoc:
+ mark_version
+
+ coder.add 'name', @name
+ coder.add 'version', @version
+ platform = case @original_platform
+ when nil, '' then
+ 'ruby'
+ when String then
+ @original_platform
+ else
+ @original_platform.to_s
+ end
+ coder.add 'platform', platform
+
+ attributes = @@attributes.map(&:to_s) - %w[name version platform]
+ attributes.each do |name|
+ coder.add name, instance_variable_get("@#{name}")
+ end
+ end
+
+ def eql? other # :nodoc:
+ self.class === other && same_attributes?(other)
+ end
+
+ ##
+ # Singular accessor for #executables
+
+ def executable
+ val = executables and val.first
+ end
+
+ ##
+ # Singular accessor for #executables
+
+ def executable=o
+ self.executables = [o]
+ end
+
+ ##
+ # Sets executables to +value+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def executables= value
+ # TODO: warn about setting instead of pushing
+ @executables = Array(value)
+ end
+
+ ##
+ # Sets extensions to +extensions+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def extensions= extensions
+ # TODO: warn about setting instead of pushing
+ @extensions = Array extensions
+ end
+
+ ##
+ # Sets extra_rdoc_files to +files+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def extra_rdoc_files= files
+ # TODO: warn about setting instead of pushing
+ @extra_rdoc_files = Array files
+ end
+
+ ##
+ # The default (generated) file name of the gem. See also #spec_name.
+ #
+ # spec.file_name # => "example-1.0.gem"
+
+ def file_name
+ "#{full_name}.gem"
+ end
+
+ ##
+ # Sets files to +files+, ensuring it is an array.
+
+ def files= files
+ @files = Array files
+ end
+
+ ##
+ # Finds all gems that satisfy +dep+
+
+ def find_all_satisfiers dep
+ Gem::Specification.each do |spec|
+ yield spec if spec.satisfies_requirement? dep
+ end
+ end
+
+ private :find_all_satisfiers
+
+ ##
+ # Creates a duplicate spec without large blobs that aren't used at runtime.
+
+ def for_cache
+ spec = dup
+
+ spec.files = nil
+ spec.test_files = nil
+
+ spec
+ end
+
+ def find_full_gem_path # :nodoc:
+ super || File.expand_path(File.join(gems_dir, original_name))
+ end
+ private :find_full_gem_path
+
+ def full_name
+ @full_name ||= super
+ end
+
+ ##
+ # The path to the gem.build_complete file within the extension install
+ # directory.
+
+ def gem_build_complete_path # :nodoc:
+ File.join extension_dir, 'gem.build_complete'
+ end
+
+ ##
+ # Work around bundler removing my methods
+
+ def gem_dir # :nodoc:
+ super
+ end
+
+ ##
+ # Deprecated and ignored, defaults to true.
+ #
+ # Formerly used to indicate this gem was RDoc-capable.
+
+ def has_rdoc # :nodoc:
+ true
+ end
+
+ ##
+ # Deprecated and ignored.
+ #
+ # Formerly used to indicate this gem was RDoc-capable.
+
+ def has_rdoc= ignored # :nodoc:
+ @has_rdoc = true
+ end
+
+ alias :has_rdoc? :has_rdoc # :nodoc:
+
+ ##
+ # True if this gem has files in test_files
+
+ def has_unit_tests? # :nodoc:
+ not test_files.empty?
+ end
+
+ # :stopdoc:
+ alias has_test_suite? has_unit_tests?
+ # :startdoc:
+
+ def hash # :nodoc:
+ name.hash ^ version.hash
+ end
+
+ def init_with coder # :nodoc:
+ @installed_by_version ||= nil
+ yaml_initialize coder.tag, coder.map
+ end
+
+ ##
+ # Specification constructor. Assigns the default values to the attributes
+ # and yields itself for further initialization. Optionally takes +name+ and
+ # +version+.
+
+ def initialize name = nil, version = nil
+ @loaded = false
+ @activated = false
+ self.loaded_from = nil
+ @original_platform = nil
+ @installed_by_version = nil
+
+ @@nil_attributes.each do |key|
+ instance_variable_set "@#{key}", nil
+ end
+
+ @@non_nil_attributes.each do |key|
+ default = default_value(key)
+ value = Dupable[key] ? default.dup : default
+ instance_variable_set "@#{key}", value
+ end
+
+ @new_platform = Gem::Platform::RUBY
+
+ self.name = name if name
+ self.version = version if version
+
+ yield self if block_given?
+ end
+
+ ##
+ # Duplicates array_attributes from +other_spec+ so state isn't shared.
+
+ def initialize_copy other_spec
+ self.class.array_attributes.each do |name|
+ name = :"@#{name}"
+ next unless other_spec.instance_variable_defined? name
+
+ begin
+ val = other_spec.instance_variable_get(name)
+ if val then
+ instance_variable_set name, val.dup
+ elsif Gem.configuration.really_verbose
+ warn "WARNING: #{full_name} has an invalid nil value for #{name}"
+ end
+ rescue TypeError
+ e = Gem::FormatException.new \
+ "#{full_name} has an invalid value for #{name}"
+
+ e.file_path = loaded_from
+ raise e
+ end
+ end
+ end
+
+ ##
+ # Expire memoized instance variables that can incorrectly generate, replace
+ # or miss files due changes in certain attributes used to compute them.
+
+ def invalidate_memoized_attributes
+ @full_name = nil
+ @cache_file = nil
+ end
+
+ private :invalidate_memoized_attributes
+
+ def inspect # :nodoc:
+ if $DEBUG
+ super
+ else
+ "#<#{self.class}:0x#{__id__.to_s(16)} #{full_name}>"
+ end
+ end
+
+ ##
+ # Returns a string usable in Dir.glob to match all requirable paths
+ # for this spec.
+
+ def lib_dirs_glob
+ dirs = if self.require_paths.size > 1 then
+ "{#{self.require_paths.join(',')}}"
+ else
+ self.require_paths.first
+ end
+
+ "#{self.full_gem_path}/#{dirs}"
+ end
+
+ ##
+ # Files in the Gem under one of the require_paths
+
+ def lib_files
+ @files.select do |file|
+ require_paths.any? do |path|
+ file.start_with? path
+ end
+ end
+ end
+
+ ##
+ # Singular accessor for #licenses
+
+ def license
+ val = licenses and val.first
+ end
+
+ ##
+ # Plural accessor for setting licenses
+ #
+ # See #license= for details
+
+ def licenses
+ @licenses ||= []
+ end
+
+ def loaded_from= path # :nodoc:
+ super
+
+ @bin_dir = nil
+ @cache_dir = nil
+ @cache_file = nil
+ @doc_dir = nil
+ @ri_dir = nil
+ @spec_dir = nil
+ @spec_file = nil
+ end
+
+ ##
+ # Sets the rubygems_version to the current RubyGems version.
+
+ def mark_version
+ @rubygems_version = Gem::VERSION
+ end
+
+ ##
+ # Return all files in this gem that match for +glob+.
+
+ def matches_for_glob glob # TODO: rename?
+ # TODO: do we need these?? Kill it
+ glob = File.join(self.lib_dirs_glob, glob)
+
+ Dir[glob].map { |f| f.untaint } # FIX our tests are broken, run w/ SAFE=1
+ end
+
+ ##
+ # Warn about unknown attributes while loading a spec.
+
+ def method_missing(sym, *a, &b) # :nodoc:
+ if @specification_version > CURRENT_SPECIFICATION_VERSION and
+ sym.to_s =~ /=$/ then
+ warn "ignoring #{sym} loading #{full_name}" if $DEBUG
+ else
+ super
+ end
+ end
+
+ ##
+ # Is this specification missing its extensions? When this returns true you
+ # probably want to build_extensions
+
+ def missing_extensions?
+ return false if default_gem?
+ return false if extensions.empty?
+ return false if installed_by_version < Gem::Version.new('2.2.0.preview.2')
+ return false if File.exist? gem_build_complete_path
+
+ true
+ end
+
+ ##
+ # Normalize the list of files so that:
+ # * All file lists have redundancies removed.
+ # * Files referenced in the extra_rdoc_files are included in the package
+ # file list.
+
+ def normalize
+ if defined?(@extra_rdoc_files) and @extra_rdoc_files then
+ @extra_rdoc_files.uniq!
+ @files ||= []
+ @files.concat(@extra_rdoc_files)
+ end
+
+ @files = @files.uniq if @files
+ @extensions = @extensions.uniq if @extensions
+ @test_files = @test_files.uniq if @test_files
+ @executables = @executables.uniq if @executables
+ @extra_rdoc_files = @extra_rdoc_files.uniq if @extra_rdoc_files
+ end
+
+ ##
+ # Return a NameTuple that represents this Specification
+
+ def name_tuple
+ Gem::NameTuple.new name, version, original_platform
+ end
+
+ ##
+ # Returns the full name (name-version) of this gemspec using the original
+ # platform. For use with legacy gems.
+
+ def original_name # :nodoc:
+ if platform == Gem::Platform::RUBY or platform.nil? then
+ "#{@name}-#{@version}"
+ else
+ "#{@name}-#{@version}-#{@original_platform}"
+ end
+ end
+
+ ##
+ # Cruft. Use +platform+.
+
+ def original_platform # :nodoc:
+ @original_platform ||= platform
+ end
+
+ ##
+ # The platform this gem runs on. See Gem::Platform for details.
+
+ def platform
+ @new_platform ||= Gem::Platform::RUBY
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.group 2, 'Gem::Specification.new do |s|', 'end' do
+ q.breakable
+
+ attributes = @@attributes - [:name, :version]
+ attributes.unshift :installed_by_version
+ attributes.unshift :version
+ attributes.unshift :name
+
+ attributes.each do |attr_name|
+ current_value = self.send attr_name
+ if current_value != default_value(attr_name) or
+ self.class.required_attribute? attr_name then
+
+ q.text "s.#{attr_name} = "
+
+ if attr_name == :date then
+ current_value = current_value.utc
+
+ q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})"
+ else
+ q.pp current_value
+ end
+
+ q.breakable
+ end
+ end
+ end
+ end
+
+ ##
+ # Raise an exception if the version of this spec conflicts with the one
+ # that is already loaded (+other+)
+
+ def check_version_conflict other # :nodoc:
+ return if self.version == other.version
+
+ # This gem is already loaded. If the currently loaded gem is not in the
+ # list of candidate gems, then we have a version conflict.
+
+ msg = "can't activate #{full_name}, already activated #{other.full_name}"
+
+ e = Gem::LoadError.new msg
+ e.name = self.name
+ # TODO: e.requirement = dep.requirement
+
+ raise e
+ end
+
+ private :check_version_conflict
+
+ ##
+ # Check the spec for possible conflicts and freak out if there are any.
+
+ def raise_if_conflicts # :nodoc:
+ conf = self.conflicts
+
+ unless conf.empty? then
+ raise Gem::ConflictError.new self, conf
+ end
+ end
+
+ ##
+ # Sets rdoc_options to +value+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def rdoc_options= options
+ # TODO: warn about setting instead of pushing
+ @rdoc_options = Array options
+ end
+
+ ##
+ # Singular accessor for #require_paths
+
+ def require_path
+ val = require_paths and val.first
+ end
+
+ ##
+ # Singular accessor for #require_paths
+
+ def require_path= path
+ self.require_paths = Array(path)
+ end
+
+ ##
+ # Set requirements to +req+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def requirements= req
+ # TODO: warn about setting instead of pushing
+ @requirements = Array req
+ end
+
+ def respond_to_missing? m, include_private = false # :nodoc:
+ false
+ end
+
+ ##
+ # Returns the full path to this spec's ri directory.
+
+ def ri_dir
+ @ri_dir ||= File.join base_dir, 'ri', full_name
+ end
+
+ ##
+ # Return a string containing a Ruby code representation of the given
+ # object.
+
+ def ruby_code(obj)
+ case obj
+ when String then obj.dump
+ when Array then '[' + obj.map { |x| ruby_code x }.join(", ") + ']'
+ when Hash then
+ seg = obj.keys.sort.map { |k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" }
+ "{ #{seg.join(', ')} }"
+ when Gem::Version then obj.to_s.dump
+ when DateLike then obj.strftime('%Y-%m-%d').dump
+ when Time then obj.strftime('%Y-%m-%d').dump
+ when Numeric then obj.inspect
+ when true, false, nil then obj.inspect
+ when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})"
+ when Gem::Requirement then
+ list = obj.as_list
+ "Gem::Requirement.new(#{ruby_code(list.size == 1 ? obj.to_s : list)})"
+ else raise Gem::Exception, "ruby_code case not handled: #{obj.class}"
+ end
+ end
+
+ private :ruby_code
+
+ ##
+ # List of dependencies that will automatically be activated at runtime.
+
+ def runtime_dependencies
+ dependencies.select { |d| d.type == :runtime }
+ end
+
+ ##
+ # True if this gem has the same attributes as +other+.
+
+ def same_attributes? spec
+ @@attributes.all? { |name, default| self.send(name) == spec.send(name) }
+ end
+
+ private :same_attributes?
+
+ ##
+ # Checks if this specification meets the requirement of +dependency+.
+
+ def satisfies_requirement? dependency
+ return @name == dependency.name &&
+ dependency.requirement.satisfied_by?(@version)
+ end
+
+ ##
+ # Returns an object you can use to sort specifications in #sort_by.
+
+ def sort_obj
+ [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ ##
+ # Used by Gem::Resolver to order Gem::Specification objects
+
+ def source # :nodoc:
+ Gem::Source::Installed.new
+ end
+
+ ##
+ # Returns the full path to the directory containing this spec's
+ # gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
+
+ def spec_dir
+ @spec_dir ||= File.join base_dir, "specifications"
+ end
+
+ ##
+ # Returns the full path to this spec's gemspec file.
+ # eg: /usr/local/lib/ruby/gems/1.8/specifications/mygem-1.0.gemspec
+
+ def spec_file
+ @spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
+ end
+
+ ##
+ # The default name of the gemspec. See also #file_name
+ #
+ # spec.spec_name # => "example-1.0.gemspec"
+
+ def spec_name
+ "#{full_name}.gemspec"
+ end
+
+ ##
+ # A short summary of this gem's description.
+
+ def summary= str
+ @summary = str.to_s.strip.
+ gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').gsub(/\n[ \t]*/, " ") # so. weird.
+ end
+
+ ##
+ # Singular accessor for #test_files
+
+ def test_file # :nodoc:
+ val = test_files and val.first
+ end
+
+ ##
+ # Singular mutator for #test_files
+
+ def test_file= file # :nodoc:
+ self.test_files = [file]
+ end
+
+ ##
+ # Test files included in this gem. You cannot append to this accessor, you
+ # must assign to it.
+
+ def test_files # :nodoc:
+ # Handle the possibility that we have @test_suite_file but not
+ # @test_files. This will happen when an old gem is loaded via
+ # YAML.
+ if defined? @test_suite_file then
+ @test_files = [@test_suite_file].flatten
+ @test_suite_file = nil
+ end
+ if defined?(@test_files) and @test_files then
+ @test_files
+ else
+ @test_files = []
+ end
+ end
+
+ ##
+ # Returns a Ruby code representation of this specification, such that it can
+ # be eval'ed and reconstruct the same specification later. Attributes that
+ # still have their default values are omitted.
+
+ def to_ruby
+ mark_version
+ result = []
+ result << "# -*- encoding: utf-8 -*-"
+ result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{raw_require_paths.join("\0")}"
+ result << "#{Gem::StubSpecification::PREFIX}#{extensions.join "\0"}" unless
+ extensions.empty?
+ result << nil
+ result << "Gem::Specification.new do |s|"
+
+ result << " s.name = #{ruby_code name}"
+ result << " s.version = #{ruby_code version}"
+ unless platform.nil? or platform == Gem::Platform::RUBY then
+ result << " s.platform = #{ruby_code original_platform}"
+ end
+ result << ""
+ result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
+
+ if metadata and !metadata.empty?
+ result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata="
+ end
+ result << " s.require_paths = #{ruby_code raw_require_paths}"
+
+ handled = [
+ :dependencies,
+ :name,
+ :platform,
+ :require_paths,
+ :required_rubygems_version,
+ :specification_version,
+ :version,
+ :has_rdoc,
+ :default_executable,
+ :metadata
+ ]
+
+ @@attributes.each do |attr_name|
+ next if handled.include? attr_name
+ current_value = self.send(attr_name)
+ if current_value != default_value(attr_name) or
+ self.class.required_attribute? attr_name then
+ result << " s.#{attr_name} = #{ruby_code current_value}"
+ end
+ end
+
+ if @installed_by_version then
+ result << nil
+ result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version"
+ end
+
+ unless dependencies.empty? then
+ result << nil
+ result << " if s.respond_to? :specification_version then"
+ result << " s.specification_version = #{specification_version}"
+ result << nil
+
+ result << " if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then"
+
+ dependencies.each do |dep|
+ req = dep.requirements_list.inspect
+ dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
+ result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{req})"
+ end
+
+ result << " else"
+
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
+
+ result << ' end'
+
+ result << " else"
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
+ result << " end"
+ end
+
+ result << "end"
+ result << nil
+
+ result.join "\n"
+ end
+
+ ##
+ # Returns a Ruby lighter-weight code representation of this specification,
+ # used for indexing only.
+ #
+ # See #to_ruby.
+
+ def to_ruby_for_cache
+ for_cache.to_ruby
+ end
+
+ def to_s # :nodoc:
+ "#<Gem::Specification name=#{@name} version=#{@version}>"
+ end
+
+ ##
+ # Returns self
+
+ def to_spec
+ self
+ end
+
+ def to_yaml(opts = {}) # :nodoc:
+ if (YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?) ||
+ (defined?(Psych) && YAML == Psych) then
+ # Because the user can switch the YAML engine behind our
+ # back, we have to check again here to make sure that our
+ # psych code was properly loaded, and load it if not.
+ unless Gem.const_defined?(:NoAliasYAMLTree)
+ require 'rubygems/psych_tree'
+ end
+
+ builder = Gem::NoAliasYAMLTree.create
+ builder << self
+ ast = builder.tree
+
+ io = Gem::StringSink.new
+ io.set_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+
+ Psych::Visitors::Emitter.new(io).accept(ast)
+
+ io.string.gsub(/ !!null \n/, " \n")
+ else
+ YAML.quick_emit object_id, opts do |out|
+ out.map taguri, to_yaml_style do |map|
+ encode_with map
+ end
+ end
+ end
+ end
+
+ ##
+ # Recursively walk dependencies of this spec, executing the +block+ for each
+ # hop.
+
+ def traverse trail = [], &block
+ trail = trail + [self]
+ runtime_dependencies.each do |dep|
+ dep.to_specs.each do |dep_spec|
+ block[self, dep, dep_spec, trail + [dep_spec]]
+ dep_spec.traverse(trail, &block) unless
+ trail.map(&:name).include? dep_spec.name
+ end
+ end
+ end
+
+ ##
+ # Checks that the specification contains all required fields, and does a
+ # very basic sanity check.
+ #
+ # Raises InvalidSpecificationException if the spec does not pass the
+ # checks..
+
+ def validate packaging = true
+ @warnings = 0
+ require 'rubygems/user_interaction'
+ extend Gem::UserInteraction
+ normalize
+
+ nil_attributes = self.class.non_nil_attributes.find_all do |attrname|
+ instance_variable_get("@#{attrname}").nil?
+ end
+
+ unless nil_attributes.empty? then
+ raise Gem::InvalidSpecificationException,
+ "#{nil_attributes.join ', '} must not be nil"
+ end
+
+ if packaging and rubygems_version != Gem::VERSION then
+ raise Gem::InvalidSpecificationException,
+ "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
+ end
+
+ @@required_attributes.each do |symbol|
+ unless self.send symbol then
+ raise Gem::InvalidSpecificationException,
+ "missing value for attribute #{symbol}"
+ end
+ end
+
+ unless String === name then
+ raise Gem::InvalidSpecificationException,
+ "invalid value for attribute name: \"#{name.inspect}\""
+ end
+
+ if raw_require_paths.empty? then
+ raise Gem::InvalidSpecificationException,
+ 'specification must have at least one require_path'
+ end
+
+ @files.delete_if { |x| File.directory?(x) }
+ @test_files.delete_if { |x| File.directory?(x) }
+ @executables.delete_if { |x| File.directory?(File.join(@bindir, x)) }
+ @extra_rdoc_files.delete_if { |x| File.directory?(x) }
+ @extensions.delete_if { |x| File.directory?(x) }
+
+ non_files = files.reject { |x| File.file?(x) }
+
+ unless not packaging or non_files.empty? then
+ raise Gem::InvalidSpecificationException,
+ "[\"#{non_files.join "\", \""}\"] are not files"
+ end
+
+ if files.include? file_name then
+ raise Gem::InvalidSpecificationException,
+ "#{full_name} contains itself (#{file_name}), check your files list"
+ end
+
+ unless specification_version.is_a?(Fixnum)
+ raise Gem::InvalidSpecificationException,
+ 'specification_version must be a Fixnum (did you mean version?)'
+ end
+
+ case platform
+ when Gem::Platform, Gem::Platform::RUBY then # ok
+ else
+ raise Gem::InvalidSpecificationException,
+ "invalid platform #{platform.inspect}, see Gem::Platform"
+ end
+
+ self.class.array_attributes.each do |field|
+ val = self.send field
+ klass = case field
+ when :dependencies
+ Gem::Dependency
+ else
+ String
+ end
+
+ unless Array === val and val.all? { |x| x.kind_of?(klass) } then
+ raise(Gem::InvalidSpecificationException,
+ "#{field} must be an Array of #{klass}")
+ end
+ end
+
+ [:authors].each do |field|
+ val = self.send field
+ raise Gem::InvalidSpecificationException, "#{field} may not be empty" if
+ val.empty?
+ end
+
+ unless Hash === metadata
+ raise Gem::InvalidSpecificationException,
+ 'metadata must be a hash'
+ end
+
+ metadata.keys.each do |k|
+ if !k.kind_of?(String)
+ raise Gem::InvalidSpecificationException,
+ 'metadata keys must be a String'
+ end
+
+ if k.size > 128
+ raise Gem::InvalidSpecificationException,
+ "metadata key too large (#{k.size} > 128)"
+ end
+ end
+
+ metadata.values.each do |k|
+ if !k.kind_of?(String)
+ raise Gem::InvalidSpecificationException,
+ 'metadata values must be a String'
+ end
+
+ if k.size > 1024
+ raise Gem::InvalidSpecificationException,
+ "metadata value too large (#{k.size} > 1024)"
+ end
+ end
+
+ licenses.each { |license|
+ if license.length > 64
+ raise Gem::InvalidSpecificationException,
+ "each license must be 64 characters or less"
+ end
+ }
+
+ warning <<-warning if licenses.empty?
+licenses is empty, but is recommended. Use a license abbreviation from:
+http://opensource.org/licenses/alphabetical
+ warning
+
+ validate_permissions
+
+ # reject lazy developers:
+
+ lazy = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
+
+ unless authors.grep(/FI XME|TO DO/x).empty? then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not an author"
+ end
+
+ unless Array(email).grep(/FI XME|TO DO/x).empty? then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not an email"
+ end
+
+ if description =~ /FI XME|TO DO/x then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not a description"
+ end
+
+ if summary =~ /FI XME|TO DO/x then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not a summary"
+ end
+
+ if homepage and not homepage.empty? and
+ homepage !~ /\A[a-z][a-z\d+.-]*:/i then
+ raise Gem::InvalidSpecificationException,
+ "\"#{homepage}\" is not a URI"
+ end
+
+ # Warnings
+
+ %w[author description email homepage summary].each do |attribute|
+ value = self.send attribute
+ warning "no #{attribute} specified" if value.nil? or value.empty?
+ end
+
+ if description == summary then
+ warning 'description and summary are identical'
+ end
+
+ # TODO: raise at some given date
+ warning "deprecated autorequire specified" if autorequire
+
+ executables.each do |executable|
+ executable_path = File.join(bindir, executable)
+ shebang = File.read(executable_path, 2) == '#!'
+
+ warning "#{executable_path} is missing #! line" unless shebang
+ end
+
+ validate_dependencies
+
+ true
+ ensure
+ if $! or @warnings > 0 then
+ alert_warning "See http://guides.rubygems.org/specification-reference/ for help"
+ end
+ end
+
+ ##
+ # Checks that dependencies use requirements as we recommend. Warnings are
+ # issued when dependencies are open-ended or overly strict for semantic
+ # versioning.
+
+ def validate_dependencies # :nodoc:
+ seen = {}
+
+ dependencies.each do |dep|
+ if prev = seen[dep.name] then
+ raise Gem::InvalidSpecificationException, <<-MESSAGE
+duplicate dependency on #{dep}, (#{prev.requirement}) use:
+ add_runtime_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
+ MESSAGE
+ end
+
+ seen[dep.name] = dep
+
+ prerelease_dep = dep.requirements_list.any? do |req|
+ Gem::Requirement.new(req).prerelease?
+ end
+
+ warning "prerelease dependency on #{dep} is not recommended" if
+ prerelease_dep
+
+ overly_strict = dep.requirement.requirements.length == 1 &&
+ dep.requirement.requirements.any? do |op, version|
+ op == '~>' and
+ not version.prerelease? and
+ version.segments.length > 2 and
+ version.segments.first != 0
+ end
+
+ if overly_strict then
+ _, dep_version = dep.requirement.requirements.first
+
+ base = dep_version.segments.first 2
+
+ warning <<-WARNING
+pessimistic dependency on #{dep} may be overly strict
+ if #{dep.name} is semantically versioned, use:
+ add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}', '>= #{dep_version}'
+ WARNING
+ end
+
+ open_ended = dep.requirement.requirements.all? do |op, version|
+ not version.prerelease? and (op == '>' or op == '>=')
+ end
+
+ if open_ended then
+ op, dep_version = dep.requirement.requirements.first
+
+ base = dep_version.segments.first 2
+
+ bugfix = if op == '>' then
+ ", '> #{dep_version}'"
+ elsif op == '>=' and base != dep_version.segments then
+ ", '>= #{dep_version}'"
+ end
+
+ warning <<-WARNING
+open-ended dependency on #{dep} is not recommended
+ if #{dep.name} is semantically versioned, use:
+ add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}
+ WARNING
+ end
+ end
+ end
+
+ ##
+ # Checks to see if the files to be packaged are world-readable.
+
+ def validate_permissions
+ return if Gem.win_platform?
+
+ files.each do |file|
+ next if File.stat(file).mode & 0444 == 0444
+ warning "#{file} is not world-readable"
+ end
+
+ executables.each do |name|
+ exec = File.join @bindir, name
+ next if File.stat(exec).executable?
+ warning "#{exec} is not executable"
+ end
+ end
+
+ ##
+ # Set the version to +version+, potentially also setting
+ # required_rubygems_version if +version+ indicates it is a
+ # prerelease.
+
+ def version= version
+ @version = Gem::Version.create(version)
+ self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
+ invalidate_memoized_attributes
+
+ return @version
+ end
+
+ def stubbed?
+ false
+ end
+
+ def yaml_initialize(tag, vals) # :nodoc:
+ vals.each do |ivar, val|
+ case ivar
+ when "date"
+ # Force Date to go through the extra coerce logic in date=
+ self.date = val.untaint
+ else
+ instance_variable_set "@#{ivar}", val.untaint
+ end
+ end
+
+ @original_platform = @platform # for backwards compatibility
+ self.platform = Gem::Platform.new @platform
+ end
+
+ ##
+ # Reset nil attributes to their default values to make the spec valid
+
+ def reset_nil_attributes_to_default
+ nil_attributes = self.class.non_nil_attributes.find_all do |name|
+ !instance_variable_defined?("@#{name}") || instance_variable_get("@#{name}").nil?
+ end
+
+ nil_attributes.each do |attribute|
+ default = self.default_value attribute
+
+ value = case default
+ when Time, Numeric, Symbol, true, false, nil then default
+ else default.dup
+ end
+
+ instance_variable_set "@#{attribute}", value
+ end
+
+ @installed_by_version ||= nil
+ end
+
+ def warning statement # :nodoc:
+ @warnings += 1
+
+ alert_warning statement
+ end
+
+ extend Gem::Deprecate
+
+ # TODO:
+ # deprecate :has_rdoc, :none, 2011, 10
+ # deprecate :has_rdoc?, :none, 2011, 10
+ # deprecate :has_rdoc=, :none, 2011, 10
+ # deprecate :default_executable, :none, 2011, 10
+ # deprecate :default_executable=, :none, 2011, 10
+ # deprecate :file_name, :cache_file, 2011, 10
+ # deprecate :full_gem_path, :cache_file, 2011, 10
+end
+
+# DOC: What is this and why is it here, randomly, at the end of this file?
+Gem.clear_paths
diff --git a/jni/ruby/lib/rubygems/ssl_certs/.document b/jni/ruby/lib/rubygems/ssl_certs/.document
new file mode 100644
index 0000000..fb66f13
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/.document
@@ -0,0 +1 @@
+# Ignore all files in this directory
diff --git a/jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem b/jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem
new file mode 100644
index 0000000..20585f1
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem
@@ -0,0 +1,25 @@
+-----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-----
diff --git a/jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem b/jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem
new file mode 100644
index 0000000..6fbdf52
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
+gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
+VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
+AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
+2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
+ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
+4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
+m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
+vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
+8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
+IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
+KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
+GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
+s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
+JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
+AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
+MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
+bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
+zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
+Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
+Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
+B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
+PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
+pu/xO28QOG8=
+-----END CERTIFICATE-----
diff --git a/jni/ruby/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem b/jni/ruby/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem
new file mode 100644
index 0000000..87676ac
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
+lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
+AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
diff --git a/jni/ruby/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem b/jni/ruby/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem
new file mode 100644
index 0000000..9e6810a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
+PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
+xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
+Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
+hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
+EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
+MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
+FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
+nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
+eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
+hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
+Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
++OkuE6N36B9K
+-----END CERTIFICATE-----
diff --git a/jni/ruby/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem b/jni/ruby/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem
new file mode 100644
index 0000000..4b8939c
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem
@@ -0,0 +1,28 @@
+-----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-----
diff --git a/jni/ruby/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem b/jni/ruby/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem
new file mode 100644
index 0000000..bcb2529
--- /dev/null
+++ b/jni/ruby/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
+R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
+9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
+fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
+iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
+1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
+MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
+ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
+uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
+Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
+tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
+hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
+5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
+-----END CERTIFICATE-----
diff --git a/jni/ruby/lib/rubygems/stub_specification.rb b/jni/ruby/lib/rubygems/stub_specification.rb
new file mode 100644
index 0000000..b184d29
--- /dev/null
+++ b/jni/ruby/lib/rubygems/stub_specification.rb
@@ -0,0 +1,196 @@
+##
+# Gem::StubSpecification reads the stub: line from the gemspec. This prevents
+# us having to eval the entire gemspec in order to find out certain
+# information.
+
+class Gem::StubSpecification < Gem::BasicSpecification
+ # :nodoc:
+ PREFIX = "# stub: "
+
+ OPEN_MODE = # :nodoc:
+ if Object.const_defined? :Encoding then
+ 'r:UTF-8:-'
+ else
+ 'r'
+ end
+
+ class StubLine # :nodoc: all
+ attr_reader :parts
+
+ def initialize(data)
+ @parts = data[PREFIX.length..-1].split(" ")
+ end
+
+ def name
+ @parts[0]
+ end
+
+ def version
+ Gem::Version.new @parts[1]
+ end
+
+ def platform
+ Gem::Platform.new @parts[2]
+ end
+
+ def require_paths
+ @parts[3..-1].join(" ").split("\0")
+ end
+ end
+
+ def initialize(filename)
+ self.loaded_from = filename
+ @data = nil
+ @extensions = nil
+ @name = nil
+ @spec = nil
+ end
+
+ ##
+ # True when this gem has been activated
+
+ def activated?
+ @activated ||=
+ begin
+ loaded = Gem.loaded_specs[name]
+ loaded && loaded.version == version
+ end
+ end
+
+ def build_extensions # :nodoc:
+ return if default_gem?
+ return if extensions.empty?
+
+ to_spec.build_extensions
+ end
+
+ ##
+ # If the gemspec contains a stubline, returns a StubLine instance. Otherwise
+ # returns the full Gem::Specification.
+
+ def data
+ unless @data
+ @extensions = []
+
+ open loaded_from, OPEN_MODE do |file|
+ begin
+ file.readline # discard encoding line
+ stubline = file.readline.chomp
+ if stubline.start_with?(PREFIX) then
+ @data = StubLine.new stubline
+
+ @extensions = $'.split "\0" if
+ /\A#{PREFIX}/ =~ file.readline.chomp
+ end
+ rescue EOFError
+ end
+ end
+ end
+
+ @data ||= to_spec
+ end
+
+ private :data
+
+ ##
+ # Extensions for this gem
+
+ def extensions
+ return @extensions if @extensions
+
+ data # load
+
+ @extensions
+ end
+
+ ##
+ # If a gem has a stub specification it doesn't need to bother with
+ # compatibility with original_name gems. It was installed with the
+ # normalized name.
+
+ def find_full_gem_path # :nodoc:
+ path = File.expand_path File.join gems_dir, full_name
+ path.untaint
+ path
+ end
+
+ ##
+ # Full paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
+ # activated.
+
+ def full_require_paths
+ @require_paths ||= data.require_paths
+
+ super
+ end
+
+ def missing_extensions?
+ return false if default_gem?
+ return false if extensions.empty?
+
+ to_spec.missing_extensions?
+ end
+
+ ##
+ # Name of the gem
+
+ def name
+ @name ||= data.name
+ end
+
+ ##
+ # Platform of the gem
+
+ def platform
+ @platform ||= data.platform
+ end
+
+ ##
+ # Require paths of the gem
+
+ def require_paths
+ @require_paths ||= data.require_paths
+
+ super
+ end
+
+ ##
+ # The full Gem::Specification for this gem, loaded from evalling its gemspec
+
+ def to_spec
+ @spec ||= if @data then
+ Gem.loaded_specs.values.find { |spec|
+ spec.name == name and spec.version == version
+ }
+ end
+
+ @spec ||= Gem::Specification.load(loaded_from)
+ @spec.ignored = @ignored if instance_variable_defined? :@ignored
+
+ @spec
+ end
+
+ ##
+ # Is this StubSpecification valid? i.e. have we found a stub line, OR does
+ # the filename contain a valid gemspec?
+
+ def valid?
+ data
+ end
+
+ ##
+ # Version of the gem
+
+ def version
+ @version ||= data.version
+ end
+
+ ##
+ # Is there a stub line present for this StubSpecification?
+
+ def stubbed?
+ data.is_a? StubLine
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/syck_hack.rb b/jni/ruby/lib/rubygems/syck_hack.rb
new file mode 100644
index 0000000..92179ab
--- /dev/null
+++ b/jni/ruby/lib/rubygems/syck_hack.rb
@@ -0,0 +1,76 @@
+# :stopdoc:
+
+# Hack to handle syck's DefaultKey bug
+#
+# This file is always loaded AFTER either syck or psych are already
+# loaded. It then looks at what constants are available and creates
+# a consistent view on all rubys.
+#
+# All this is so that there is always a YAML::Syck::DefaultKey
+# class no matter if the full yaml library has loaded or not.
+#
+
+module YAML # :nodoc:
+ # In newer 1.9.2, there is a Syck toplevel constant instead of it
+ # being underneath YAML. If so, reference it back under YAML as
+ # well.
+ if defined? ::Syck
+ # for tests that change YAML::ENGINE
+ # 1.8 does not support the second argument to const_defined?
+ remove_const :Syck rescue nil
+
+ Syck = ::Syck
+
+ # JRuby's "Syck" is called "Yecht"
+ elsif defined? YAML::Yecht
+ Syck = YAML::Yecht
+
+ # Otherwise, if there is no YAML::Syck, then we've got just psych
+ # loaded, so lets define a stub for DefaultKey.
+ elsif !defined? YAML::Syck
+ module Syck
+ class DefaultKey # :nodoc:
+ end
+ end
+ end
+
+ # Now that we've got something that is always here, define #to_s
+ # so when code tries to use this, it at least just shows up like it
+ # should.
+ module Syck
+ class DefaultKey
+ remove_method :to_s rescue nil
+
+ def to_s
+ '='
+ end
+ end
+ end
+
+ SyntaxError = Error unless defined? SyntaxError
+end
+
+# Sometime in the 1.9 dev cycle, the Syck constant was moved from under YAML
+# to be a toplevel constant. So gemspecs created under these versions of Syck
+# will have references to Syck::DefaultKey.
+#
+# So we need to be sure that we reference Syck at the toplevel too so that
+# we can always load these kind of gemspecs.
+#
+if !defined?(Syck)
+ Syck = YAML::Syck
+end
+
+# Now that we've got Syck setup in all the right places, store
+# a reference to the DefaultKey class inside Gem. We do this so that
+# if later on YAML, etc are redefined, we've still got a consistent
+# place to find the DefaultKey class for comparison.
+
+module Gem
+ # for tests that change YAML::ENGINE
+ remove_const :SyckDefaultKey if const_defined? :SyckDefaultKey
+
+ SyckDefaultKey = YAML::Syck::DefaultKey
+end
+
+# :startdoc:
diff --git a/jni/ruby/lib/rubygems/test_case.rb b/jni/ruby/lib/rubygems/test_case.rb
new file mode 100644
index 0000000..5dc7a1b
--- /dev/null
+++ b/jni/ruby/lib/rubygems/test_case.rb
@@ -0,0 +1,1430 @@
+# TODO: $SAFE = 1
+
+begin
+ gem 'minitest', '~> 4.0'
+rescue NoMethodError, Gem::LoadError
+ # for ruby tests
+end
+
+if defined? Gem::QuickLoader
+ Gem::QuickLoader.load_full_rubygems_library
+else
+ require 'rubygems'
+end
+
+begin
+ gem 'minitest'
+rescue Gem::LoadError
+end
+
+# We have to load these up front because otherwise we'll try to load
+# them while we're testing rubygems, and thus we can't actually load them.
+unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
+ gem 'rdoc'
+ gem 'json'
+end
+
+require 'minitest/autorun'
+
+require 'rubygems/deprecate'
+
+require 'fileutils'
+require 'pathname'
+require 'pp'
+require 'rubygems/package'
+require 'shellwords'
+require 'tmpdir'
+require 'uri'
+require 'zlib'
+
+Gem.load_yaml
+
+require 'rubygems/mock_gem_ui'
+
+module Gem
+
+ ##
+ # Allows setting the gem path searcher. This method is available when
+ # requiring 'rubygems/test_case'
+
+ def self.searcher=(searcher)
+ @searcher = searcher
+ end
+
+ ##
+ # Allows toggling Windows behavior. This method is available when requiring
+ # 'rubygems/test_case'
+
+ def self.win_platform=(val)
+ @@win_platform = val
+ end
+
+ ##
+ # Allows setting path to Ruby. This method is available when requiring
+ # 'rubygems/test_case'
+
+ def self.ruby= ruby
+ @ruby = ruby
+ end
+
+ ##
+ # When rubygems/test_case is required the default user interaction is a
+ # MockGemUi.
+
+ module DefaultUserInteraction
+ @ui = Gem::MockGemUi.new
+ end
+end
+
+##
+# RubyGemTestCase provides a variety of methods for testing rubygems and
+# gem-related behavior in a sandbox. Through RubyGemTestCase you can install
+# and uninstall gems, fetch remote gems through a stub fetcher and be assured
+# your normal set of gems is not affected.
+#
+# Tests are always run at a safe level of 1.
+
+class Gem::TestCase < MiniTest::Unit::TestCase
+
+ attr_accessor :fetcher # :nodoc:
+
+ attr_accessor :gem_repo # :nodoc:
+
+ attr_accessor :uri # :nodoc:
+
+ def assert_activate expected, *specs
+ specs.each do |spec|
+ case spec
+ when String then
+ Gem::Specification.find_by_name(spec).activate
+ when Gem::Specification then
+ spec.activate
+ else
+ flunk spec.inspect
+ end
+ end
+
+ loaded = Gem.loaded_specs.values.map(&:full_name)
+
+ assert_equal expected.sort, loaded.sort if expected
+ end
+
+ # TODO: move to minitest
+ def assert_path_exists path, msg = nil
+ msg = message(msg) { "Expected path '#{path}' to exist" }
+ assert File.exist?(path), msg
+ end
+
+ ##
+ # Sets the ENABLE_SHARED entry in RbConfig::CONFIG to +value+ and restores
+ # the original value when the block ends
+
+ def enable_shared value
+ enable_shared = RbConfig::CONFIG['ENABLE_SHARED']
+ RbConfig::CONFIG['ENABLE_SHARED'] = value
+
+ yield
+ ensure
+ if enable_shared then
+ RbConfig::CONFIG['enable_shared'] = enable_shared
+ else
+ RbConfig::CONFIG.delete 'enable_shared'
+ end
+ end
+
+ # TODO: move to minitest
+ def refute_path_exists path, msg = nil
+ msg = message(msg) { "Expected path '#{path}' to not exist" }
+ refute File.exist?(path), msg
+ end
+
+ def scan_make_command_lines(output)
+ output.scan(/^#{Regexp.escape make_command}(?:[[:blank:]].*)?$/)
+ end
+
+ def parse_make_command_line(line)
+ command, *args = line.shellsplit
+
+ targets = []
+ macros = {}
+
+ args.each do |arg|
+ case arg
+ when /\A(\w+)=/
+ macros[$1] = $'
+ else
+ targets << arg
+ end
+ end
+
+ targets << '' if targets.empty?
+
+ {
+ :command => command,
+ :targets => targets,
+ :macros => macros,
+ }
+ end
+
+ def assert_contains_make_command(target, output, msg = nil)
+ if output.match(/\n/)
+ msg = message(msg) {
+ 'Expected output containing make command "%s": %s' % [
+ ('%s %s' % [make_command, target]).rstrip,
+ output.inspect
+ ]
+ }
+ else
+ msg = message(msg) {
+ 'Expected make command "%s": %s' % [
+ ('%s %s' % [make_command, target]).rstrip,
+ output.inspect
+ ]
+ }
+ end
+
+ assert scan_make_command_lines(output).any? { |line|
+ make = parse_make_command_line(line)
+
+ if make[:targets].include?(target)
+ yield make, line if block_given?
+ true
+ else
+ false
+ end
+ }, msg
+ end
+
+ include Gem::DefaultUserInteraction
+
+ undef_method :default_test if instance_methods.include? 'default_test' or
+ instance_methods.include? :default_test
+
+ @@project_dir = Dir.pwd.untaint unless defined?(@@project_dir)
+
+ @@initial_reset = false
+
+ ##
+ # #setup prepares a sandboxed location to install gems. All installs are
+ # directed to a temporary directory. All install plugins are removed.
+ #
+ # If the +RUBY+ environment variable is set the given path is used for
+ # Gem::ruby. The local platform is set to <tt>i386-mswin32</tt> for Windows
+ # or <tt>i686-darwin8.10.1</tt> otherwise.
+ #
+ # If the +KEEP_FILES+ environment variable is set the files will not be
+ # removed from <tt>/tmp/test_rubygems_#{$$}.#{Time.now.to_i}</tt>.
+
+ def setup
+ super
+
+ @orig_gem_home = ENV['GEM_HOME']
+ @orig_gem_path = ENV['GEM_PATH']
+ @orig_gem_vendor = ENV['GEM_VENDOR']
+
+ ENV['GEM_VENDOR'] = nil
+
+ @current_dir = Dir.pwd
+ @fetcher = nil
+ @ui = Gem::MockGemUi.new
+
+ tmpdir = File.expand_path Dir.tmpdir
+ tmpdir.untaint
+
+ if ENV['KEEP_FILES'] then
+ @tempdir = File.join(tmpdir, "test_rubygems_#{$$}.#{Time.now.to_i}")
+ else
+ @tempdir = File.join(tmpdir, "test_rubygems_#{$$}")
+ end
+ @tempdir.untaint
+
+ FileUtils.mkdir_p @tempdir
+
+ # This makes the tempdir consistent on OS X.
+ # File.expand_path Dir.tmpdir #=> "/var/..."
+ # Dir.chdir Dir.tmpdir do File.expand_path '.' end #=> "/private/var/..."
+ # TODO use File#realpath above instead of #expand_path once 1.8 support is
+ # dropped.
+ Dir.chdir @tempdir do
+ @tempdir = File.expand_path '.'
+ @tempdir.untaint
+ end
+
+ @gemhome = File.join @tempdir, 'gemhome'
+ @userhome = File.join @tempdir, 'userhome'
+ ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache'
+
+ @orig_ruby = if ENV['RUBY'] then
+ ruby = Gem.ruby
+ Gem.ruby = ENV['RUBY']
+ ruby
+ end
+
+ @git = ENV['GIT'] || 'git'
+
+ Gem.ensure_gem_subdirectories @gemhome
+
+ @orig_LOAD_PATH = $LOAD_PATH.dup
+ $LOAD_PATH.map! { |s| File.expand_path(s).untaint }
+
+ Dir.chdir @tempdir
+
+ @orig_ENV_HOME = ENV['HOME']
+ ENV['HOME'] = @userhome
+ Gem.instance_variable_set :@user_home, nil
+ Gem.send :remove_instance_variable, :@ruby_version if
+ Gem.instance_variables.include? :@ruby_version
+
+ FileUtils.mkdir_p @gemhome
+ FileUtils.mkdir_p @userhome
+
+ @orig_gem_private_key_passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
+ ENV['GEM_PRIVATE_KEY_PASSPHRASE'] = PRIVATE_KEY_PASSPHRASE
+
+ @default_dir = File.join @tempdir, 'default'
+ @default_spec_dir = File.join @default_dir, "specifications", "default"
+ Gem.instance_variable_set :@default_dir, @default_dir
+ FileUtils.mkdir_p @default_spec_dir
+
+ # We use Gem::Specification.reset the first time only so that if there
+ # are unresolved deps that leak into the whole test suite, they're at least
+ # reported once.
+ if @@initial_reset
+ Gem::Specification.unresolved_deps.clear # done to avoid cross-test warnings
+ else
+ @@initial_reset = true
+ Gem::Specification.reset
+ end
+ Gem.use_paths(@gemhome)
+
+ Gem::Security.reset
+
+ Gem.loaded_specs.clear
+ Gem.clear_default_specs
+ Gem::Specification.unresolved_deps.clear
+
+ Gem.configuration.verbose = true
+ Gem.configuration.update_sources = true
+
+ Gem::RemoteFetcher.fetcher = Gem::FakeFetcher.new
+
+ @gem_repo = "http://gems.example.com/"
+ @uri = URI.parse @gem_repo
+ Gem.sources.replace [@gem_repo]
+
+ Gem.searcher = nil
+ Gem::SpecFetcher.fetcher = nil
+ @orig_BASERUBY = RbConfig::CONFIG['BASERUBY']
+ RbConfig::CONFIG['BASERUBY'] = RbConfig::CONFIG['ruby_install_name']
+
+ @orig_arch = RbConfig::CONFIG['arch']
+
+ if win_platform?
+ util_set_arch 'i386-mswin32'
+ else
+ util_set_arch 'i686-darwin8.10.1'
+ end
+
+ @marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
+ end
+
+ ##
+ # #teardown restores the process to its original state and removes the
+ # tempdir unless the +KEEP_FILES+ environment variable was set.
+
+ def teardown
+ $LOAD_PATH.replace @orig_LOAD_PATH if @orig_LOAD_PATH
+
+ if @orig_BASERUBY
+ RbConfig::CONFIG['BASERUBY'] = @orig_BASERUBY
+ else
+ RbConfig::CONFIG.delete('BASERUBY')
+ end
+ RbConfig::CONFIG['arch'] = @orig_arch
+
+ if defined? Gem::RemoteFetcher then
+ Gem::RemoteFetcher.fetcher = nil
+ end
+
+ Dir.chdir @current_dir
+
+ FileUtils.rm_rf @tempdir unless ENV['KEEP_FILES']
+
+ ENV['GEM_HOME'] = @orig_gem_home
+ ENV['GEM_PATH'] = @orig_gem_path
+ ENV['GEM_VENDOR'] = @orig_gem_vendor
+
+ Gem.ruby = @orig_ruby if @orig_ruby
+
+ if @orig_ENV_HOME then
+ ENV['HOME'] = @orig_ENV_HOME
+ else
+ ENV.delete 'HOME'
+ end
+
+ Gem.instance_variable_set :@default_dir, nil
+
+ ENV['GEM_PRIVATE_KEY_PASSPHRASE'] = @orig_gem_private_key_passphrase
+
+ Gem::Specification._clear_load_cache
+ end
+
+ def common_installer_setup
+ common_installer_teardown
+
+ Gem.post_build do |installer|
+ @post_build_hook_arg = installer
+ true
+ end
+
+ Gem.post_install do |installer|
+ @post_install_hook_arg = installer
+ end
+
+ Gem.post_uninstall do |uninstaller|
+ @post_uninstall_hook_arg = uninstaller
+ end
+
+ Gem.pre_install do |installer|
+ @pre_install_hook_arg = installer
+ true
+ end
+
+ Gem.pre_uninstall do |uninstaller|
+ @pre_uninstall_hook_arg = uninstaller
+ end
+ end
+
+ def common_installer_teardown
+ Gem.post_build_hooks.clear
+ Gem.post_install_hooks.clear
+ Gem.done_installing_hooks.clear
+ Gem.post_reset_hooks.clear
+ Gem.post_uninstall_hooks.clear
+ Gem.pre_install_hooks.clear
+ Gem.pre_reset_hooks.clear
+ Gem.pre_uninstall_hooks.clear
+ end
+
+ ##
+ # A git_gem is used with a gem dependencies file. The gem created here
+ # has no files, just a gem specification for the given +name+ and +version+.
+ #
+ # Yields the +specification+ to the block, if given
+
+ def git_gem name = 'a', version = 1
+ have_git?
+
+ directory = File.join 'git', name
+ directory = File.expand_path directory
+
+ git_spec = Gem::Specification.new name, version do |specification|
+ yield specification if block_given?
+ end
+
+ FileUtils.mkdir_p directory
+
+ gemspec = "#{name}.gemspec"
+
+ open File.join(directory, gemspec), 'w' do |io|
+ io.write git_spec.to_ruby
+ end
+
+ head = nil
+
+ Dir.chdir directory do
+ unless File.exist? '.git' then
+ system @git, 'init', '--quiet'
+ system @git, 'config', 'user.name', 'RubyGems Tests'
+ system @git, 'config', 'user.email', 'rubygems@example'
+ end
+
+ system @git, 'add', gemspec
+ system @git, 'commit', '-a', '-m', 'a non-empty commit message', '--quiet'
+ head = Gem::Util.popen('git', 'rev-parse', 'master').strip
+ end
+
+ return name, git_spec.version, directory, head
+ end
+
+ ##
+ # Skips this test unless you have a git executable
+
+ def have_git?
+ return if in_path? @git
+
+ skip 'cannot find git executable, use GIT environment variable to set'
+ end
+
+ def in_path? executable # :nodoc:
+ return true if %r%\A([A-Z]:|/)% =~ executable and File.exist? executable
+
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
+ File.exist? File.join directory, executable
+ end
+ end
+
+ ##
+ # Builds and installs the Gem::Specification +spec+
+
+ def install_gem spec, options = {}
+ require 'rubygems/installer'
+
+ gem = File.join @tempdir, "gems", "#{spec.full_name}.gem"
+
+ unless File.exist? gem then
+ use_ui Gem::MockGemUi.new do
+ Dir.chdir @tempdir do
+ Gem::Package.build spec
+ end
+ end
+
+ gem = File.join(@tempdir, File.basename(spec.cache_file)).untaint
+ end
+
+ Gem::Installer.new(gem, options.merge({:wrappers => true})).install
+ end
+
+ ##
+ # Builds and installs the Gem::Specification +spec+ into the user dir
+
+ def install_gem_user spec
+ install_gem spec, :user_install => true
+ end
+
+ ##
+ # Uninstalls the Gem::Specification +spec+
+ def uninstall_gem spec
+ require 'rubygems/uninstaller'
+
+ Gem::Uninstaller.new(spec.name,
+ :executables => true, :user_install => true).uninstall
+ end
+
+ ##
+ # creates a temporary directory with hax
+ # TODO: deprecate and remove
+
+ def create_tmpdir
+ tmpdir = nil
+ Dir.chdir Dir.tmpdir do tmpdir = Dir.pwd end # HACK OSX /private/tmp
+ tmpdir = File.join tmpdir, "test_rubygems_#{$$}"
+ FileUtils.mkdir_p tmpdir
+ return tmpdir
+ end
+
+ ##
+ # Enables pretty-print for all tests
+
+ def mu_pp(obj)
+ s = ''
+ s = PP.pp obj, s
+ s = s.force_encoding(Encoding.default_external) if defined? Encoding
+ s.chomp
+ end
+
+ ##
+ # Reads a Marshal file at +path+
+
+ def read_cache(path)
+ open path.dup.untaint, 'rb' do |io|
+ Marshal.load io.read
+ end
+ end
+
+ ##
+ # Reads a binary file at +path+
+
+ def read_binary(path)
+ Gem.read_binary path
+ end
+
+ ##
+ # Writes a binary file to +path+ which is relative to +@gemhome+
+
+ def write_file(path)
+ path = File.join @gemhome, path unless Pathname.new(path).absolute?
+ dir = File.dirname path
+ FileUtils.mkdir_p dir
+
+ open path, 'wb' do |io|
+ yield io if block_given?
+ end
+
+ path
+ end
+
+ def all_spec_names
+ Gem::Specification.map(&:full_name)
+ end
+
+ ##
+ # Creates a Gem::Specification with a minimum of extra work. +name+ and
+ # +version+ are the gem's name and version, platform, author, email,
+ # homepage, summary and description are defaulted. The specification is
+ # yielded for customization.
+ #
+ # The gem is added to the installed gems in +@gemhome+ and the runtime.
+ #
+ # Use this with #write_file to build an installed gem.
+
+ def quick_gem(name, version='2')
+ require 'rubygems/specification'
+
+ spec = Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = name
+ s.version = version
+ s.author = 'A User'
+ s.email = 'example@example.com'
+ s.homepage = 'http://example.com'
+ s.summary = "this is a summary"
+ s.description = "This is a test description"
+
+ yield(s) if block_given?
+ end
+
+ Gem::Specification.map # HACK: force specs to (re-)load before we write
+
+ written_path = write_file spec.spec_file do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ spec.loaded_from = spec.loaded_from = written_path
+
+ Gem::Specification.add_spec spec.for_cache
+
+ return spec
+ end
+
+ ##
+ # TODO: remove in RubyGems 3.0
+
+ def quick_spec name, version = '2' # :nodoc:
+ util_spec name, version
+ end
+
+ ##
+ # Builds a gem from +spec+ and places it in <tt>File.join @gemhome,
+ # 'cache'</tt>. Automatically creates files based on +spec.files+
+
+ def util_build_gem(spec)
+ dir = spec.gem_dir
+ FileUtils.mkdir_p dir
+
+ Dir.chdir dir do
+ spec.files.each do |file|
+ next if File.exist? file
+ FileUtils.mkdir_p File.dirname(file)
+ File.open file, 'w' do |fp| fp.puts "# #{file}" end
+ end
+
+ use_ui Gem::MockGemUi.new do
+ Gem::Package.build spec
+ end
+
+ cache = spec.cache_file
+ FileUtils.mv File.basename(cache), cache
+ end
+ end
+
+ def util_remove_gem(spec)
+ FileUtils.rm_rf spec.cache_file
+ FileUtils.rm_rf spec.spec_file
+ end
+
+ ##
+ # Removes all installed gems from +@gemhome+.
+
+ def util_clear_gems
+ FileUtils.rm_rf File.join(@gemhome, "gems") # TODO: use Gem::Dirs
+ FileUtils.mkdir File.join(@gemhome, "gems")
+ FileUtils.rm_rf File.join(@gemhome, "specifications")
+ FileUtils.mkdir File.join(@gemhome, "specifications")
+ Gem::Specification.reset
+ end
+
+ ##
+ # Install the provided specs
+
+ def install_specs(*specs)
+ Gem::Specification.add_specs(*specs)
+ Gem.searcher = nil
+ end
+
+ ##
+ # Installs the provided default specs including writing the spec file
+
+ def install_default_gems(*specs)
+ install_default_specs(*specs)
+
+ specs.each do |spec|
+ open spec.loaded_from, 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+ end
+ end
+
+ ##
+ # Install the provided default specs
+
+ def install_default_specs(*specs)
+ install_specs(*specs)
+ specs.each do |spec|
+ Gem.register_default_spec(spec)
+ end
+ end
+
+ def loaded_spec_names
+ Gem.loaded_specs.values.map(&:full_name).sort
+ end
+
+ def unresolved_names
+ Gem::Specification.unresolved_deps.values.map(&:to_s).sort
+ end
+
+ def save_loaded_features
+ old_loaded_features = $LOADED_FEATURES.dup
+ yield
+ ensure
+ $LOADED_FEATURES.replace old_loaded_features
+ end
+
+ ##
+ # new_spec is deprecated as it is never used.
+ #
+ # TODO: remove in RubyGems 3.0
+
+ def new_spec name, version, deps = nil, *files # :nodoc:
+ require 'rubygems/specification'
+
+ spec = Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = name
+ s.version = version
+ s.author = 'A User'
+ s.email = 'example@example.com'
+ s.homepage = 'http://example.com'
+ s.summary = "this is a summary"
+ s.description = "This is a test description"
+
+ Array(deps).each do |n, req|
+ s.add_dependency n, (req || '>= 0')
+ end
+
+ s.files.push(*files) unless files.empty?
+
+ yield s if block_given?
+ end
+
+ spec.loaded_from = spec.spec_file
+
+ unless files.empty? then
+ write_file spec.spec_file do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ util_build_gem spec
+
+ cache_file = File.join @tempdir, 'gems', "#{spec.full_name}.gem"
+ FileUtils.mkdir_p File.dirname cache_file
+ FileUtils.mv spec.cache_file, cache_file
+ FileUtils.rm spec.spec_file
+ end
+
+ spec
+ end
+
+ def new_default_spec(name, version, deps = nil, *files)
+ spec = util_spec name, version, deps
+
+ spec.loaded_from = File.join(@default_spec_dir, spec.spec_name)
+ spec.files = files
+
+ lib_dir = File.join(@tempdir, "default_gems", "lib")
+ $LOAD_PATH.unshift(lib_dir)
+ files.each do |file|
+ rb_path = File.join(lib_dir, file)
+ FileUtils.mkdir_p(File.dirname(rb_path))
+ File.open(rb_path, "w") do |rb|
+ rb << "# #{file}"
+ end
+ end
+
+ spec
+ end
+
+ ##
+ # Creates a spec with +name+, +version+. +deps+ can specify the dependency
+ # or a +block+ can be given for full customization of the specification.
+
+ def util_spec name, version = 2, deps = nil # :yields: specification
+ raise "deps or block, not both" if deps and block_given?
+
+ spec = Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = name
+ s.version = version
+ s.author = 'A User'
+ s.email = 'example@example.com'
+ s.homepage = 'http://example.com'
+ s.summary = "this is a summary"
+ s.description = "This is a test description"
+
+ yield s if block_given?
+ end
+
+ if deps then
+ # Since Hash#each is unordered in 1.8, sort the keys and iterate that
+ # way so the tests are deterministic on all implementations.
+ deps.keys.sort.each do |n|
+ spec.add_dependency n, (deps[n] || '>= 0')
+ end
+ end
+
+ spec.loaded_from = spec.spec_file
+
+ Gem::Specification.add_spec spec
+
+ return spec
+ end
+
+ ##
+ # Creates a gem with +name+, +version+ and +deps+. The specification will
+ # be yielded before gem creation for customization. The gem will be placed
+ # in <tt>File.join @tempdir, 'gems'</tt>. The specification and .gem file
+ # location are returned.
+
+ def util_gem(name, version, deps = nil, &block)
+ # TODO: deprecate
+ raise "deps or block, not both" if deps and block
+
+ if deps then
+ block = proc do |s|
+ # Since Hash#each is unordered in 1.8, sort
+ # the keys and iterate that way so the tests are
+ # deterministic on all implementations.
+ deps.keys.sort.each do |n|
+ s.add_dependency n, (deps[n] || '>= 0')
+ end
+ end
+ end
+
+ spec = quick_gem(name, version, &block)
+
+ util_build_gem spec
+
+ cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
+ FileUtils.mkdir_p File.dirname cache_file
+ FileUtils.mv spec.cache_file, cache_file
+ FileUtils.rm spec.spec_file
+
+ spec.loaded_from = nil
+
+ [spec, cache_file]
+ end
+
+ ##
+ # Gzips +data+.
+
+ def util_gzip(data)
+ out = StringIO.new
+
+ Zlib::GzipWriter.wrap out do |io|
+ io.write data
+ end
+
+ out.string
+ end
+
+ ##
+ # Creates several default gems which all have a lib/code.rb file. The gems
+ # are not installed but are available in the cache dir.
+ #
+ # +@a1+:: gem a version 1, this is the best-described gem.
+ # +@a2+:: gem a version 2
+ # +@a3a:: gem a version 3.a
+ # +@a_evil9+:: gem a_evil version 9, use this to ensure similarly-named gems
+ # don't collide with a.
+ # +@b2+:: gem b version 2
+ # +@c1_2+:: gem c version 1.2
+ # +@pl1+:: gem pl version 1, this gem has a legacy platform of i386-linux.
+ #
+ # Additional +prerelease+ gems may also be created:
+ #
+ # +@a2_pre+:: gem a version 2.a
+ # TODO: nuke this and fix tests. this should speed up a lot
+
+ def util_make_gems(prerelease = false)
+ @a1 = quick_gem 'a', '1' do |s|
+ s.files = %w[lib/code.rb]
+ s.require_paths = %w[lib]
+ s.date = Gem::Specification::TODAY - 86400
+ s.homepage = 'http://a.example.com'
+ s.email = %w[example@example.com example2@example.com]
+ s.authors = %w[Example Example2]
+ s.description = <<-DESC
+This line is really, really long. So long, in fact, that it is more than eighty characters long! The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters. Without the wrapping, the text might not look good in the RSS feed.
+
+Also, a list:
+ * An entry that\'s actually kind of sort
+ * an entry that\'s really long, which will probably get wrapped funny. That's ok, somebody wasn't thinking straight when they made it more than eighty characters.
+ DESC
+ end
+
+ init = proc do |s|
+ s.files = %w[lib/code.rb]
+ s.require_paths = %w[lib]
+ end
+
+ @a2 = quick_gem('a', '2', &init)
+ @a3a = quick_gem('a', '3.a', &init)
+ @a_evil9 = quick_gem('a_evil', '9', &init)
+ @b2 = quick_gem('b', '2', &init)
+ @c1_2 = quick_gem('c', '1.2', &init)
+ @x = quick_gem('x', '1', &init)
+ @dep_x = quick_gem('dep_x', '1') do |s|
+ s.files = %w[lib/code.rb]
+ s.require_paths = %w[lib]
+ s.add_dependency 'x', '>= 1'
+ end
+
+ @pl1 = quick_gem 'pl', '1' do |s| # l for legacy
+ s.files = %w[lib/code.rb]
+ s.require_paths = %w[lib]
+ s.platform = Gem::Platform.new 'i386-linux'
+ s.instance_variable_set :@original_platform, 'i386-linux'
+ end
+
+ if prerelease
+ @a2_pre = quick_gem('a', '2.a', &init)
+ write_file File.join(*%W[gems #{@a2_pre.original_name} lib code.rb])
+ util_build_gem @a2_pre
+ end
+
+ write_file File.join(*%W[gems #{@a1.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@a2.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@a3a.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@a_evil9.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@b2.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@c1_2.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@pl1.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@x.original_name} lib code.rb])
+ write_file File.join(*%W[gems #{@dep_x.original_name} lib code.rb])
+
+ [@a1, @a2, @a3a, @a_evil9, @b2, @c1_2, @pl1, @x, @dep_x].each do |spec|
+ util_build_gem spec
+ end
+
+ FileUtils.rm_r File.join(@gemhome, "gems", @pl1.original_name)
+ end
+
+ ##
+ # Set the platform to +arch+
+
+ def util_set_arch(arch)
+ RbConfig::CONFIG['arch'] = arch
+ platform = Gem::Platform.new arch
+
+ Gem.instance_variable_set :@platforms, nil
+ Gem::Platform.instance_variable_set :@local, nil
+
+ platform
+ end
+
+ ##
+ # Sets up a fake fetcher using the gems from #util_make_gems. Optionally
+ # additional +prerelease+ gems may be included.
+ #
+ # Gems created by this method may be fetched using Gem::RemoteFetcher.
+
+ def util_setup_fake_fetcher(prerelease = false)
+ require 'zlib'
+ require 'socket'
+ require 'rubygems/remote_fetcher'
+
+ @fetcher = Gem::FakeFetcher.new
+
+ util_make_gems(prerelease)
+ Gem::Specification.reset
+
+ @all_gems = [@a1, @a2, @a3a, @a_evil9, @b2, @c1_2].sort
+ @all_gem_names = @all_gems.map { |gem| gem.full_name }
+
+ gem_names = [@a1.full_name, @a2.full_name, @a3a.full_name, @b2.full_name]
+ @gem_names = gem_names.sort.join("\n")
+
+ Gem::RemoteFetcher.fetcher = @fetcher
+ end
+
+ ##
+ # Add +spec+ to +@fetcher+ serving the data in the file +path+.
+ # +repo+ indicates which repo to make +spec+ appear to be in.
+
+ def add_to_fetcher(spec, path=nil, repo=@gem_repo)
+ path ||= spec.cache_file
+ @fetcher.data["#{@gem_repo}gems/#{spec.file_name}"] = read_binary(path)
+ end
+
+ ##
+ # Sets up Gem::SpecFetcher to return information from the gems in +specs+.
+ # Best used with +@all_gems+ from #util_setup_fake_fetcher.
+
+ def util_setup_spec_fetcher(*specs)
+ specs -= Gem::Specification._all
+ Gem::Specification.add_specs(*specs)
+
+ spec_fetcher = Gem::SpecFetcher.fetcher
+
+ prerelease, all = Gem::Specification.partition { |spec|
+ spec.version.prerelease?
+ }
+
+ spec_fetcher.specs[@uri] = []
+ all.each do |spec|
+ spec_fetcher.specs[@uri] << spec.name_tuple
+ end
+
+ spec_fetcher.latest_specs[@uri] = []
+ Gem::Specification.latest_specs.each do |spec|
+ spec_fetcher.latest_specs[@uri] << spec.name_tuple
+ end
+
+ spec_fetcher.prerelease_specs[@uri] = []
+ prerelease.each do |spec|
+ spec_fetcher.prerelease_specs[@uri] << spec.name_tuple
+ end
+
+ # HACK for test_download_to_cache
+ unless Gem::RemoteFetcher === @fetcher then
+ v = Gem.marshal_version
+
+ specs = all.map { |spec| spec.name_tuple }
+ s_zip = util_gzip Marshal.dump Gem::NameTuple.to_basic specs
+
+ latest_specs = Gem::Specification.latest_specs.map do |spec|
+ spec.name_tuple
+ end
+
+ l_zip = util_gzip Marshal.dump Gem::NameTuple.to_basic latest_specs
+
+ prerelease_specs = prerelease.map { |spec| spec.name_tuple }
+ p_zip = util_gzip Marshal.dump Gem::NameTuple.to_basic prerelease_specs
+
+ @fetcher.data["#{@gem_repo}specs.#{v}.gz"] = s_zip
+ @fetcher.data["#{@gem_repo}latest_specs.#{v}.gz"] = l_zip
+ @fetcher.data["#{@gem_repo}prerelease_specs.#{v}.gz"] = p_zip
+
+ v = Gem.marshal_version
+
+ Gem::Specification.each do |spec|
+ path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
+ data = Marshal.dump spec
+ data_deflate = Zlib::Deflate.deflate data
+ @fetcher.data[path] = data_deflate
+ end
+ end
+
+ nil # force errors
+ end
+
+ ##
+ # Deflates +data+
+
+ def util_zip(data)
+ Zlib::Deflate.deflate data
+ end
+
+ def util_set_RUBY_VERSION(version, patchlevel = nil, revision = nil)
+ if Gem.instance_variables.include? :@ruby_version or
+ Gem.instance_variables.include? '@ruby_version' then
+ Gem.send :remove_instance_variable, :@ruby_version
+ end
+
+ @RUBY_VERSION = RUBY_VERSION
+ @RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
+ @RUBY_REVISION = RUBY_REVISION if defined?(RUBY_REVISION)
+
+ Object.send :remove_const, :RUBY_VERSION
+ Object.send :remove_const, :RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
+ Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+
+ Object.const_set :RUBY_VERSION, version
+ Object.const_set :RUBY_PATCHLEVEL, patchlevel if patchlevel
+ Object.const_set :RUBY_REVISION, revision if revision
+ end
+
+ def util_restore_RUBY_VERSION
+ Object.send :remove_const, :RUBY_VERSION
+ Object.send :remove_const, :RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
+ Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
+
+ Object.const_set :RUBY_VERSION, @RUBY_VERSION
+ Object.const_set :RUBY_PATCHLEVEL, @RUBY_PATCHLEVEL if
+ defined?(@RUBY_PATCHLEVEL)
+ Object.const_set :RUBY_REVISION, @RUBY_REVISION if
+ defined?(@RUBY_REVISION)
+ end
+
+ ##
+ # Is this test being run on a Windows platform?
+
+ def self.win_platform?
+ Gem.win_platform?
+ end
+
+ ##
+ # Is this test being run on a Windows platform?
+
+ def win_platform?
+ Gem.win_platform?
+ end
+
+ ##
+ # Returns whether or not we're on a version of Ruby built with VC++ (or
+ # Borland) versus Cygwin, Mingw, etc.
+
+ def self.vc_windows?
+ RUBY_PLATFORM.match('mswin')
+ end
+
+ ##
+ # Returns whether or not we're on a version of Ruby built with VC++ (or
+ # Borland) versus Cygwin, Mingw, etc.
+
+ def vc_windows?
+ RUBY_PLATFORM.match('mswin')
+ end
+
+ ##
+ # Returns the make command for the current platform. For versions of Ruby
+ # built on MS Windows with VC++ or Borland it will return 'nmake'. On all
+ # other platforms, including Cygwin, it will return 'make'.
+
+ def self.make_command
+ ENV["make"] || (vc_windows? ? 'nmake' : 'make')
+ end
+
+ ##
+ # Returns the make command for the current platform. For versions of Ruby
+ # built on MS Windows with VC++ or Borland it will return 'nmake'. On all
+ # other platforms, including Cygwin, it will return 'make'.
+
+ def make_command
+ ENV["make"] || (vc_windows? ? 'nmake' : 'make')
+ end
+
+ ##
+ # Returns whether or not the nmake command could be found.
+
+ def nmake_found?
+ system('nmake /? 1>NUL 2>&1')
+ end
+
+ # In case we're building docs in a background process, this method waits for
+ # that process to exit (or if it's already been reaped, or never happened,
+ # swallows the Errno::ECHILD error).
+ def wait_for_child_process_to_exit
+ Process.wait if Process.respond_to?(:fork)
+ rescue Errno::ECHILD
+ end
+
+ ##
+ # Allows tests to use a random (but controlled) port number instead of
+ # a hardcoded one. This helps CI tools when running parallels builds on
+ # the same builder slave.
+
+ def self.process_based_port
+ @@process_based_port ||= 8000 + $$ % 1000
+ end
+
+ ##
+ # See ::process_based_port
+
+ def process_based_port
+ self.class.process_based_port
+ end
+
+ ##
+ # Allows the proper version of +rake+ to be used for the test.
+
+ def build_rake_in(good=true)
+ gem_ruby = Gem.ruby
+ Gem.ruby = @@ruby
+ env_rake = ENV["rake"]
+ rake = (good ? @@good_rake : @@bad_rake)
+ ENV["rake"] = rake
+ yield rake
+ ensure
+ Gem.ruby = gem_ruby
+ if env_rake
+ ENV["rake"] = env_rake
+ else
+ ENV.delete("rake")
+ end
+ end
+
+ ##
+ # Finds the path to the Ruby executable
+
+ def self.rubybin
+ ruby = ENV["RUBY"]
+ return ruby if ruby
+ ruby = "ruby"
+ rubyexe = "#{ruby}.exe"
+
+ 3.times do
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
+ return File.expand_path(ruby)
+ end
+ if File.exist? rubyexe and File.executable? rubyexe
+ return File.expand_path(rubyexe)
+ end
+ ruby = File.join("..", ruby)
+ end
+
+ begin
+ require "rbconfig"
+ File.join(RbConfig::CONFIG["bindir"],
+ RbConfig::CONFIG["ruby_install_name"] +
+ RbConfig::CONFIG["EXEEXT"])
+ rescue LoadError
+ "ruby"
+ end
+ end
+
+ @@ruby = rubybin
+ @@good_rake = "#{rubybin} #{File.expand_path('../../../test/rubygems/good_rake.rb', __FILE__)}"
+ @@bad_rake = "#{rubybin} #{File.expand_path('../../../test/rubygems/bad_rake.rb', __FILE__)}"
+
+ ##
+ # Construct a new Gem::Dependency.
+
+ def dep name, *requirements
+ Gem::Dependency.new name, *requirements
+ end
+
+ ##
+ # Constructs a Gem::Resolver::DependencyRequest from a
+ # Gem::Dependency +dep+, a +from_name+ and +from_version+ requesting the
+ # dependency and a +parent+ DependencyRequest
+
+ def dependency_request dep, from_name, from_version, parent = nil
+ remote = Gem::Source.new @uri
+
+ unless parent then
+ parent_dep = dep from_name, from_version
+ parent = Gem::Resolver::DependencyRequest.new parent_dep, nil
+ end
+
+ spec = Gem::Resolver::IndexSpecification.new \
+ nil, from_name, from_version, remote, Gem::Platform::RUBY
+ activation = Gem::Resolver::ActivationRequest.new spec, parent
+
+ Gem::Resolver::DependencyRequest.new dep, activation
+ end
+
+ ##
+ # Constructs a new Gem::Requirement.
+
+ def req *requirements
+ return requirements.first if Gem::Requirement === requirements.first
+ Gem::Requirement.create requirements
+ end
+
+ ##
+ # Constructs a new Gem::Specification.
+
+ def spec name, version, &block
+ Gem::Specification.new name, v(version), &block
+ end
+
+ ##
+ # Creates a SpecFetcher pre-filled with the gems or specs defined in the
+ # block.
+ #
+ # Yields a +fetcher+ object that responds to +spec+ and +gem+. +spec+ adds
+ # a specification to the SpecFetcher while +gem+ adds both a specification
+ # and the gem data to the RemoteFetcher so the built gem can be downloaded.
+ #
+ # If only the a-3 gem is supposed to be downloaded you can save setup
+ # time by creating only specs for the other versions:
+ #
+ # spec_fetcher do |fetcher|
+ # fetcher.spec 'a', 1
+ # fetcher.spec 'a', 2, 'b' => 3 # dependency on b = 3
+ # fetcher.gem 'a', 3 do |spec|
+ # # spec is a Gem::Specification
+ # # ...
+ # end
+ # end
+
+ def spec_fetcher repository = @gem_repo
+ Gem::TestCase::SpecFetcherSetup.declare self, repository do |spec_fetcher_setup|
+ yield spec_fetcher_setup if block_given?
+ end
+ end
+
+ ##
+ # Construct a new Gem::Version.
+
+ def v string
+ Gem::Version.create string
+ end
+
+ ##
+ # A vendor_gem is used with a gem dependencies file. The gem created here
+ # has no files, just a gem specification for the given +name+ and +version+.
+ #
+ # Yields the +specification+ to the block, if given
+
+ def vendor_gem name = 'a', version = 1
+ directory = File.join 'vendor', name
+
+ vendor_spec = Gem::Specification.new name, version do |specification|
+ yield specification if block_given?
+ end
+
+ FileUtils.mkdir_p directory
+
+ open File.join(directory, "#{name}.gemspec"), 'w' do |io|
+ io.write vendor_spec.to_ruby
+ end
+
+ return name, vendor_spec.version, directory
+ end
+
+ ##
+ # The StaticSet is a static set of gem specifications used for testing only.
+ # It is available by requiring Gem::TestCase.
+
+ class StaticSet < Gem::Resolver::Set
+
+ ##
+ # A StaticSet ignores remote because it has a fixed set of gems.
+
+ attr_accessor :remote
+
+ ##
+ # Creates a new StaticSet for the given +specs+
+
+ def initialize(specs)
+ super()
+
+ @specs = specs
+
+ @remote = true
+ end
+
+ ##
+ # Adds +spec+ to this set.
+
+ def add spec
+ @specs << spec
+ end
+
+ ##
+ # Finds +dep+ in this set.
+
+ def find_spec(dep)
+ @specs.reverse_each do |s|
+ return s if dep.matches_spec? s
+ end
+ end
+
+ ##
+ # Finds all gems matching +dep+ in this set.
+
+ def find_all(dep)
+ @specs.find_all { |s| dep.match? s, @prerelease }
+ end
+
+ ##
+ # Loads a Gem::Specification from this set which has the given +name+,
+ # version +ver+, +platform+. The +source+ is ignored.
+
+ def load_spec name, ver, platform, source
+ dep = Gem::Dependency.new name, ver
+ spec = find_spec dep
+
+ Gem::Specification.new spec.name, spec.version do |s|
+ s.platform = spec.platform
+ end
+ end
+
+ def prefetch reqs # :nodoc:
+ end
+ end
+
+ ##
+ # Loads certificate named +cert_name+ from <tt>test/rubygems/</tt>.
+
+ def self.load_cert cert_name
+ cert_file = cert_path cert_name
+
+ cert = File.read cert_file
+
+ OpenSSL::X509::Certificate.new cert
+ end
+
+ ##
+ # Returns the path to the certificate named +cert_name+ from
+ # <tt>test/rubygems/</tt>.
+
+ def self.cert_path cert_name
+ if 32 == (Time.at(2**32) rescue 32) then
+ cert_file =
+ File.expand_path "../../../test/rubygems/#{cert_name}_cert_32.pem",
+ __FILE__
+
+ return cert_file if File.exist? cert_file
+ end
+
+ File.expand_path "../../../test/rubygems/#{cert_name}_cert.pem", __FILE__
+ end
+
+ ##
+ # Loads an RSA private key named +key_name+ with +passphrase+ in <tt>test/rubygems/</tt>
+
+ def self.load_key key_name, passphrase = nil
+ key_file = key_path key_name
+
+ key = File.read key_file
+
+ OpenSSL::PKey::RSA.new key, passphrase
+ end
+
+ ##
+ # Returns the path to the key named +key_name+ from <tt>test/rubygems</tt>
+
+ def self.key_path key_name
+ File.expand_path "../../../test/rubygems/#{key_name}_key.pem", __FILE__
+ end
+
+ # :stopdoc:
+ # only available in RubyGems tests
+
+ PRIVATE_KEY_PASSPHRASE = 'Foo bar'
+
+ begin
+ PRIVATE_KEY = load_key 'private'
+ PRIVATE_KEY_PATH = key_path 'private'
+
+ # ENCRYPTED_PRIVATE_KEY is PRIVATE_KEY encrypted with PRIVATE_KEY_PASSPHRASE
+ ENCRYPTED_PRIVATE_KEY = load_key 'encrypted_private', PRIVATE_KEY_PASSPHRASE
+ ENCRYPTED_PRIVATE_KEY_PATH = key_path 'encrypted_private'
+
+ PUBLIC_KEY = PRIVATE_KEY.public_key
+
+ PUBLIC_CERT = load_cert 'public'
+ PUBLIC_CERT_PATH = cert_path 'public'
+ rescue Errno::ENOENT
+ PRIVATE_KEY = nil
+ PUBLIC_KEY = nil
+ PUBLIC_CERT = nil
+ end if defined?(OpenSSL::SSL)
+
+end
+
+require 'rubygems/test_utilities'
+
diff --git a/jni/ruby/lib/rubygems/test_utilities.rb b/jni/ruby/lib/rubygems/test_utilities.rb
new file mode 100644
index 0000000..25786e6
--- /dev/null
+++ b/jni/ruby/lib/rubygems/test_utilities.rb
@@ -0,0 +1,383 @@
+require 'tempfile'
+require 'rubygems'
+require 'rubygems/remote_fetcher'
+
+##
+# A fake Gem::RemoteFetcher for use in tests or to avoid real live HTTP
+# requests when testing code that uses RubyGems.
+#
+# Example:
+#
+# @fetcher = Gem::FakeFetcher.new
+# @fetcher.data['http://gems.example.com/yaml'] = source_index.to_yaml
+# Gem::RemoteFetcher.fetcher = @fetcher
+#
+# # invoke RubyGems code
+#
+# paths = @fetcher.paths
+# assert_equal 'http://gems.example.com/yaml', paths.shift
+# assert paths.empty?, paths.join(', ')
+#
+# See RubyGems' tests for more examples of FakeFetcher.
+
+class Gem::FakeFetcher
+
+ attr_reader :data
+ attr_reader :last_request
+ attr_reader :api_endpoints
+ attr_accessor :paths
+
+ def initialize
+ @data = {}
+ @paths = []
+ @api_endpoints = {}
+ end
+
+ def api_endpoint(uri)
+ @api_endpoints[uri] || uri
+ end
+
+ def find_data(path)
+ return File.read path.path if URI === path and 'file' == path.scheme
+
+ if URI === path and "URI::#{path.scheme.upcase}" != path.class.name then
+ raise ArgumentError,
+ "mismatch for scheme #{path.scheme} and class #{path.class}"
+ end
+
+ path = path.to_s
+ @paths << path
+ raise ArgumentError, 'need full URI' unless path =~ %r'^https?://'
+
+ unless @data.key? path then
+ raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
+ end
+
+ @data[path]
+ end
+
+ def fetch_path path, mtime = nil, head = false
+ data = find_data(path)
+
+ if data.respond_to?(:call) then
+ data.call
+ else
+ if path.to_s =~ /gz$/ and not data.nil? and not data.empty? then
+ data = Gem.gunzip data
+ end
+
+ data
+ end
+ end
+
+ def cache_update_path uri, path = nil, update = true
+ if data = fetch_path(uri)
+ open(path, 'wb') { |io| io.write data } if path and update
+ data
+ else
+ Gem.read_binary(path) if path
+ end
+ end
+
+ # Thanks, FakeWeb!
+ def open_uri_or_path(path)
+ data = find_data(path)
+ body, code, msg = data
+
+ response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
+ response.instance_variable_set(:@body, body)
+ response.instance_variable_set(:@read, true)
+ response
+ end
+
+ def request(uri, request_class, last_modified = nil)
+ data = find_data(uri)
+ body, code, msg = data
+
+ @last_request = request_class.new uri.request_uri
+ yield @last_request if block_given?
+
+ response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
+ response.instance_variable_set(:@body, body)
+ response.instance_variable_set(:@read, true)
+ response
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[FakeFetcher', ']' do
+ q.breakable
+ q.text 'URIs:'
+
+ q.breakable
+ q.pp @data.keys
+
+ unless @api_endpoints.empty? then
+ q.breakable
+ q.text 'API endpoints:'
+
+ q.breakable
+ q.pp @api_endpoints.keys
+ end
+ end
+ end
+
+ def fetch_size(path)
+ path = path.to_s
+ @paths << path
+
+ raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
+
+ unless @data.key? path then
+ raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
+ end
+
+ data = @data[path]
+
+ data.respond_to?(:call) ? data.call : data.length
+ end
+
+ def download spec, source_uri, install_dir = Gem.dir
+ name = File.basename spec.cache_file
+ path = if Dir.pwd == install_dir then # see fetch_command
+ install_dir
+ else
+ File.join install_dir, "cache"
+ end
+
+ path = File.join path, name
+
+ if source_uri =~ /^http/ then
+ File.open(path, "wb") do |f|
+ f.write fetch_path(File.join(source_uri, "gems", name))
+ end
+ else
+ FileUtils.cp source_uri, path
+ end
+
+ path
+ end
+
+ def download_to_cache dependency
+ found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
+
+ return if found.empty?
+
+ spec, source = found.first
+
+ download spec, source.uri.to_s
+ end
+
+end
+
+# :stopdoc:
+class Gem::RemoteFetcher
+
+ def self.fetcher=(fetcher)
+ @fetcher = fetcher
+ end
+
+end
+# :startdoc:
+
+##
+# The SpecFetcherSetup allows easy setup of a remote source in RubyGems tests:
+#
+# spec_fetcher do |f|
+# f.gem 'a', 1
+# f.spec 'a', 2
+# f.gem 'b', 1' 'a' => '~> 1.0'
+# f.clear
+# end
+#
+# The above declaration creates two gems, a-1 and b-1, with a dependency from
+# b to a. The declaration creates an additional spec a-2, but no gem for it
+# (so it cannot be installed).
+#
+# After the gems are created they are removed from Gem.dir.
+
+class Gem::TestCase::SpecFetcherSetup
+
+ ##
+ # Executes a SpecFetcher setup block. Yields an instance then creates the
+ # gems and specifications defined in the instance.
+
+ def self.declare test, repository
+ setup = new test, repository
+
+ yield setup
+
+ setup.execute
+ end
+
+ def initialize test, repository # :nodoc:
+ @test = test
+ @repository = repository
+
+ @gems = {}
+ @installed = []
+ @operations = []
+ end
+
+ ##
+ # Removes any created gems or specifications from Gem.dir (the default
+ # install location).
+
+ def clear
+ @operations << [:clear]
+ end
+
+ ##
+ # Returns a Hash of created Specification full names and the corresponding
+ # Specification.
+
+ def created_specs
+ created = {}
+
+ @gems.keys.each do |spec|
+ created[spec.full_name] = spec
+ end
+
+ created
+ end
+
+ ##
+ # Creates any defined gems or specifications
+
+ def execute # :nodoc:
+ execute_operations
+
+ setup_fetcher
+
+ created_specs
+ end
+
+ def execute_operations # :nodoc:
+ @operations.each do |operation, *arguments|
+ case operation
+ when :clear then
+ @test.util_clear_gems
+ @installed.clear
+ when :gem then
+ spec, gem = @test.util_gem(*arguments, &arguments.pop)
+
+ write_spec spec
+
+ @gems[spec] = gem
+ @installed << spec
+ when :spec then
+ spec = @test.util_spec(*arguments, &arguments.pop)
+
+ write_spec spec
+
+ @gems[spec] = nil
+ @installed << spec
+ end
+ end
+ end
+
+ ##
+ # Creates a gem with +name+, +version+ and +deps+. The created gem can be
+ # downloaded and installed.
+ #
+ # The specification will be yielded before gem creation for customization,
+ # but only the block or the dependencies may be set, not both.
+
+ def gem name, version, dependencies = nil, &block
+ @operations << [:gem, name, version, dependencies, block]
+ end
+
+ ##
+ # Creates a legacy platform spec with the name 'pl' and version 1
+
+ def legacy_platform
+ spec 'pl', 1 do |s|
+ s.platform = Gem::Platform.new 'i386-linux'
+ s.instance_variable_set :@original_platform, 'i386-linux'
+ end
+ end
+
+ def setup_fetcher # :nodoc:
+ require 'zlib'
+ require 'socket'
+ require 'rubygems/remote_fetcher'
+
+ unless @test.fetcher then
+ @test.fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @test.fetcher
+ end
+
+ Gem::Specification.reset
+
+ begin
+ gem_repo, @test.gem_repo = @test.gem_repo, @repository
+ @test.uri = URI @repository
+
+ @test.util_setup_spec_fetcher(*@gems.keys)
+ ensure
+ @test.gem_repo = gem_repo
+ @test.uri = URI gem_repo
+ end
+
+ # This works around util_setup_spec_fetcher adding all created gems to the
+ # installed set.
+ Gem::Specification.reset
+ Gem::Specification.add_specs(*@installed)
+
+ @gems.each do |spec, gem|
+ next unless gem
+
+ @test.fetcher.data["#{@repository}gems/#{spec.file_name}"] =
+ Gem.read_binary(gem)
+
+ FileUtils.cp gem, spec.cache_file
+ end
+ end
+
+ ##
+ # Creates a spec with +name+, +version+ and +deps+. The created gem can be
+ # downloaded and installed.
+ #
+ # The specification will be yielded before creation for customization,
+ # but only the block or the dependencies may be set, not both.
+
+ def spec name, version, dependencies = nil, &block
+ @operations << [:spec, name, version, dependencies, block]
+ end
+
+ def write_spec spec # :nodoc:
+ open spec.spec_file, 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+ end
+
+end
+
+##
+# A StringIO duck-typed class that uses Tempfile instead of String as the
+# backing store.
+#
+# This is available when rubygems/test_utilities is required.
+#--
+# This class was added to flush out problems in Rubinius' IO implementation.
+
+class TempIO < Tempfile
+
+ ##
+ # Creates a new TempIO that will be initialized to contain +string+.
+
+ def initialize(string = '')
+ super "TempIO"
+ binmode
+ write string
+ rewind
+ end
+
+ ##
+ # The content of the TempIO as a String.
+
+ def string
+ flush
+ Gem.read_binary path
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/text.rb b/jni/ruby/lib/rubygems/text.rb
new file mode 100644
index 0000000..5c9287a
--- /dev/null
+++ b/jni/ruby/lib/rubygems/text.rb
@@ -0,0 +1,75 @@
+require 'rubygems'
+
+##
+# A collection of text-wrangling methods
+
+module Gem::Text
+
+ ##
+ # Wraps +text+ to +wrap+ characters and optionally indents by +indent+
+ # characters
+
+ def format_text(text, wrap, indent=0)
+ result = []
+ work = text.dup
+
+ while work.length > wrap do
+ if work =~ /^(.{0,#{wrap}})[ \n]/ then
+ result << $1.rstrip
+ work.slice!(0, $&.length)
+ else
+ result << work.slice!(0, wrap)
+ end
+ end
+
+ result << work if work.length.nonzero?
+ result.join("\n").gsub(/^/, " " * indent)
+ end
+
+ def min3 a, b, c # :nodoc:
+ if a < b && a < c then
+ a
+ elsif b < c then
+ b
+ else
+ c
+ end
+ end
+
+ # This code is based directly on the Text gem implementation
+ # Returns a value representing the "cost" of transforming str1 into str2
+ def levenshtein_distance str1, str2
+ s = str1
+ t = str2
+ n = s.length
+ m = t.length
+ max = n/2
+
+ return m if (0 == n)
+ return n if (0 == m)
+ return n if (n - m).abs > max
+
+ d = (0..m).to_a
+ x = nil
+
+ str1.each_char.each_with_index do |char1,i|
+ e = i+1
+
+ str2.each_char.each_with_index do |char2,j|
+ cost = (char1 == char2) ? 0 : 1
+ x = min3(
+ d[j+1] + 1, # insertion
+ e + 1, # deletion
+ d[j] + cost # substitution
+ )
+ d[j] = e
+ e = x
+ end
+
+ d[m] = x
+ end
+
+ return x
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/uninstaller.rb b/jni/ruby/lib/rubygems/uninstaller.rb
new file mode 100644
index 0000000..2a6edc6
--- /dev/null
+++ b/jni/ruby/lib/rubygems/uninstaller.rb
@@ -0,0 +1,345 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'fileutils'
+require 'rubygems'
+require 'rubygems/dependency_list'
+require 'rubygems/rdoc'
+require 'rubygems/user_interaction'
+
+##
+# An Uninstaller.
+#
+# The uninstaller fires pre and post uninstall hooks. Hooks can be added
+# either through a rubygems_plugin.rb file in an installed gem or via a
+# rubygems/defaults/#{RUBY_ENGINE}.rb or rubygems/defaults/operating_system.rb
+# file. See Gem.pre_uninstall and Gem.post_uninstall for details.
+
+class Gem::Uninstaller
+
+ include Gem::UserInteraction
+
+ ##
+ # The directory a gem's executables will be installed into
+
+ attr_reader :bin_dir
+
+ ##
+ # The gem repository the gem will be installed into
+
+ attr_reader :gem_home
+
+ ##
+ # The Gem::Specification for the gem being uninstalled, only set during
+ # #uninstall_gem
+
+ attr_reader :spec
+
+ ##
+ # Constructs an uninstaller that will uninstall +gem+
+
+ def initialize(gem, options = {})
+ # TODO document the valid options
+ @gem = gem
+ @version = options[:version] || Gem::Requirement.default
+ @gem_home = File.expand_path(options[:install_dir] || Gem.dir)
+ @force_executables = options[:executables]
+ @force_all = options[:all]
+ @force_ignore = options[:ignore]
+ @bin_dir = options[:bin_dir]
+ @format_executable = options[:format_executable]
+ @abort_on_dependent = options[:abort_on_dependent]
+
+ # Indicate if development dependencies should be checked when
+ # uninstalling. (default: false)
+ #
+ @check_dev = options[:check_dev]
+
+ if options[:force]
+ @force_all = true
+ @force_ignore = true
+ end
+
+ # only add user directory if install_dir is not set
+ @user_install = false
+ @user_install = options[:user_install] unless options[:install_dir]
+ end
+
+ ##
+ # Performs the uninstall of the gem. This removes the spec, the Gem
+ # directory, and the cached .gem file.
+
+ def uninstall
+ dependency = Gem::Dependency.new @gem, @version
+
+ list = []
+
+ dirs =
+ Gem::Specification.dirs +
+ [Gem::Specification.default_specifications_dir]
+
+ Gem::Specification.each_spec dirs do |spec|
+ next unless dependency.matches_spec? spec
+
+ list << spec
+ end
+
+ default_specs, list = list.partition do |spec|
+ spec.default_gem?
+ end
+
+ list, other_repo_specs = list.partition do |spec|
+ @gem_home == spec.base_dir or
+ (@user_install and spec.base_dir == Gem.user_dir)
+ end
+
+ list.sort!
+
+ if list.empty? then
+ if other_repo_specs.empty?
+ if default_specs.empty?
+ raise Gem::InstallError, "gem #{@gem.inspect} is not installed"
+ else
+ message =
+ "gem #{@gem.inspect} cannot be uninstalled " +
+ "because it is a default gem"
+ raise Gem::InstallError, message
+ end
+ end
+
+ other_repos = other_repo_specs.map { |spec| spec.base_dir }.uniq
+
+ message = ["#{@gem} is not installed in GEM_HOME, try:"]
+ message.concat other_repos.map { |repo|
+ "\tgem uninstall -i #{repo} #{@gem}"
+ }
+
+ raise Gem::InstallError, message.join("\n")
+ elsif @force_all then
+ remove_all list
+
+ elsif list.size > 1 then
+ gem_names = list.map { |gem| gem.full_name }
+ gem_names << "All versions"
+
+ say
+ _, index = choose_from_list "Select gem to uninstall:", gem_names
+
+ if index == list.size then
+ remove_all list
+ elsif index >= 0 && index < list.size then
+ uninstall_gem list[index]
+ else
+ say "Error: must enter a number [1-#{list.size+1}]"
+ end
+ else
+ uninstall_gem list.first
+ end
+ end
+
+ ##
+ # Uninstalls gem +spec+
+
+ def uninstall_gem(spec)
+ @spec = spec
+
+ unless dependencies_ok? spec
+ if abort_on_dependent? || !ask_if_ok(spec)
+ raise Gem::DependencyRemovalException,
+ "Uninstallation aborted due to dependent gem(s)"
+ end
+ end
+
+ Gem.pre_uninstall_hooks.each do |hook|
+ hook.call self
+ end
+
+ remove_executables @spec
+ remove @spec
+
+ Gem.post_uninstall_hooks.each do |hook|
+ hook.call self
+ end
+
+ @spec = nil
+ end
+
+ ##
+ # Removes installed executables and batch files (windows only) for
+ # +gemspec+.
+
+ def remove_executables(spec)
+ return if spec.nil? or spec.executables.empty?
+
+ executables = spec.executables.clone
+
+ # Leave any executables created by other installed versions
+ # of this gem installed.
+
+ list = Gem::Specification.find_all { |s|
+ s.name == spec.name && s.version != spec.version
+ }
+
+ list.each do |s|
+ s.executables.each do |exe_name|
+ executables.delete exe_name
+ end
+ end
+
+ return if executables.empty?
+
+ executables = executables.map { |exec| formatted_program_filename exec }
+
+ remove = if @force_executables.nil? then
+ ask_yes_no("Remove executables:\n" +
+ "\t#{executables.join ', '}\n\n" +
+ "in addition to the gem?",
+ true)
+ else
+ @force_executables
+ end
+
+ if remove then
+ bin_dir = @bin_dir || Gem.bindir(spec.base_dir)
+
+ raise Gem::FilePermissionError, bin_dir unless File.writable? bin_dir
+
+ executables.each do |exe_name|
+ say "Removing #{exe_name}"
+
+ exe_file = File.join bin_dir, exe_name
+
+ FileUtils.rm_f exe_file
+ FileUtils.rm_f "#{exe_file}.bat"
+ end
+ else
+ say "Executables and scripts will remain installed."
+ end
+ end
+
+ ##
+ # Removes all gems in +list+.
+ #
+ # NOTE: removes uninstalled gems from +list+.
+
+ def remove_all(list)
+ list.each { |spec| uninstall_gem spec }
+ end
+
+ ##
+ # spec:: the spec of the gem to be uninstalled
+ # list:: the list of all such gems
+ #
+ # Warning: this method modifies the +list+ parameter. Once it has
+ # uninstalled a gem, it is removed from that list.
+
+ def remove(spec)
+ unless path_ok?(@gem_home, spec) or
+ (@user_install and path_ok?(Gem.user_dir, spec)) then
+ e = Gem::GemNotInHomeException.new \
+ "Gem '#{spec.full_name}' is not installed in directory #{@gem_home}"
+ e.spec = spec
+
+ raise e
+ end
+
+ raise Gem::FilePermissionError, spec.base_dir unless
+ File.writable?(spec.base_dir)
+
+ FileUtils.rm_rf spec.full_gem_path
+ FileUtils.rm_rf spec.extension_dir
+
+ old_platform_name = spec.original_name
+ gemspec = spec.spec_file
+
+ unless File.exist? gemspec then
+ gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec")
+ end
+
+ FileUtils.rm_rf gemspec
+
+ gem = spec.cache_file
+ gem = File.join(spec.cache_dir, "#{old_platform_name}.gem") unless
+ File.exist? gem
+
+ FileUtils.rm_rf gem
+
+ Gem::RDoc.new(spec).remove
+
+ say "Successfully uninstalled #{spec.full_name}"
+
+ Gem::Specification.remove_spec spec
+ end
+
+ ##
+ # Is +spec+ in +gem_dir+?
+
+ def path_ok?(gem_dir, spec)
+ full_path = File.join gem_dir, 'gems', spec.full_name
+ original_path = File.join gem_dir, 'gems', spec.original_name
+
+ full_path == spec.full_gem_path || original_path == spec.full_gem_path
+ end
+
+ ##
+ # Returns true if it is OK to remove +spec+ or this is a forced
+ # uninstallation.
+
+ def dependencies_ok? spec # :nodoc:
+ return true if @force_ignore
+
+ deplist = Gem::DependencyList.from_specs
+ deplist.ok_to_remove?(spec.full_name, @check_dev)
+ end
+
+ ##
+ # Should the uninstallation abort if a dependency will go unsatisfied?
+ #
+ # See ::new.
+
+ def abort_on_dependent? # :nodoc:
+ @abort_on_dependent
+ end
+
+ ##
+ # Asks if it is OK to remove +spec+. Returns true if it is OK.
+
+ def ask_if_ok spec # :nodoc:
+ msg = ['']
+ msg << 'You have requested to uninstall the gem:'
+ msg << "\t#{spec.full_name}"
+ msg << ''
+
+ siblings = Gem::Specification.select do |s|
+ s.name == spec.name && s.full_name != spec.full_name
+ end
+
+ spec.dependent_gems.each do |dep_spec, dep, satlist|
+ unless siblings.any? { |s| s.satisfies_requirement? dep }
+ msg << "#{dep_spec.name}-#{dep_spec.version} depends on #{dep}"
+ end
+ end
+
+ msg << 'If you remove this gem, these dependencies will not be met.'
+ msg << 'Continue with Uninstall?'
+ return ask_yes_no(msg.join("\n"), false)
+ end
+
+ ##
+ # Returns the formatted version of the executable +filename+
+
+ def formatted_program_filename filename # :nodoc:
+ # TODO perhaps the installer should leave a small manifest
+ # of what it did for us to find rather than trying to recreate
+ # it again.
+ if @format_executable then
+ require 'rubygems/installer'
+ Gem::Installer.exec_format % File.basename(filename)
+ else
+ filename
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/uri_formatter.rb b/jni/ruby/lib/rubygems/uri_formatter.rb
new file mode 100644
index 0000000..68aacc6
--- /dev/null
+++ b/jni/ruby/lib/rubygems/uri_formatter.rb
@@ -0,0 +1,49 @@
+require 'cgi'
+require 'uri'
+
+##
+# The UriFormatter handles URIs from user-input and escaping.
+#
+# uf = Gem::UriFormatter.new 'example.com'
+#
+# p uf.normalize #=> 'http://example.com'
+
+class Gem::UriFormatter
+
+ ##
+ # The URI to be formatted.
+
+ attr_reader :uri
+
+ ##
+ # Creates a new URI formatter for +uri+.
+
+ def initialize uri
+ @uri = uri
+ end
+
+ ##
+ # Escapes the #uri for use as a CGI parameter
+
+ def escape
+ return unless @uri
+ CGI.escape @uri
+ end
+
+ ##
+ # Normalize the URI by adding "http://" if it is missing.
+
+ def normalize
+ (@uri =~ /^(https?|ftp|file):/i) ? @uri : "http://#{@uri}"
+ end
+
+ ##
+ # Unescapes the #uri which came from a CGI parameter
+
+ def unescape
+ return unless @uri
+ CGI.unescape @uri
+ end
+
+end
+
diff --git a/jni/ruby/lib/rubygems/user_interaction.rb b/jni/ruby/lib/rubygems/user_interaction.rb
new file mode 100644
index 0000000..44ff2d3
--- /dev/null
+++ b/jni/ruby/lib/rubygems/user_interaction.rb
@@ -0,0 +1,711 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+begin
+ require 'io/console'
+rescue LoadError
+end
+
+##
+# Module that defines the default UserInteraction. Any class including this
+# module will have access to the +ui+ method that returns the default UI.
+
+module Gem::DefaultUserInteraction
+
+ ##
+ # The default UI is a class variable of the singleton class for this
+ # module.
+
+ @ui = nil
+
+ ##
+ # Return the default UI.
+
+ def self.ui
+ @ui ||= Gem::ConsoleUI.new
+ end
+
+ ##
+ # Set the default UI. If the default UI is never explicitly set, a simple
+ # console based UserInteraction will be used automatically.
+
+ def self.ui=(new_ui)
+ @ui = new_ui
+ end
+
+ ##
+ # Use +new_ui+ for the duration of +block+.
+
+ def self.use_ui(new_ui)
+ old_ui = @ui
+ @ui = new_ui
+ yield
+ ensure
+ @ui = old_ui
+ end
+
+ ##
+ # See DefaultUserInteraction::ui
+
+ def ui
+ Gem::DefaultUserInteraction.ui
+ end
+
+ ##
+ # See DefaultUserInteraction::ui=
+
+ def ui=(new_ui)
+ Gem::DefaultUserInteraction.ui = new_ui
+ end
+
+ ##
+ # See DefaultUserInteraction::use_ui
+
+ def use_ui(new_ui, &block)
+ Gem::DefaultUserInteraction.use_ui(new_ui, &block)
+ end
+
+end
+
+##
+# UserInteraction allows RubyGems to interact with the user through standard
+# methods that can be replaced with more-specific UI methods for different
+# displays.
+#
+# Since UserInteraction dispatches to a concrete UI class you may need to
+# reference other classes for specific behavior such as Gem::ConsoleUI or
+# Gem::SilentUI.
+#
+# Example:
+#
+# class X
+# include Gem::UserInteraction
+#
+# def get_answer
+# n = ask("What is the meaning of life?")
+# end
+# end
+
+module Gem::UserInteraction
+
+ include Gem::DefaultUserInteraction
+
+ ##
+ # Displays an alert +statement+. Asks a +question+ if given.
+
+ def alert statement, question = nil
+ ui.alert statement, question
+ end
+
+ ##
+ # Displays an error +statement+ to the error output location. Asks a
+ # +question+ if given.
+
+ def alert_error statement, question = nil
+ ui.alert_error statement, question
+ end
+
+ ##
+ # Displays a warning +statement+ to the warning output location. Asks a
+ # +question+ if given.
+
+ def alert_warning statement, question = nil
+ ui.alert_warning statement, question
+ end
+
+ ##
+ # Asks a +question+ and returns the answer.
+
+ def ask question
+ ui.ask question
+ end
+
+ ##
+ # Asks for a password with a +prompt+
+
+ def ask_for_password prompt
+ ui.ask_for_password prompt
+ end
+
+ ##
+ # Asks a yes or no +question+. Returns true for yes, false for no.
+
+ def ask_yes_no question, default = nil
+ ui.ask_yes_no question, default
+ end
+
+ ##
+ # Asks the user to answer +question+ with an answer from the given +list+.
+
+ def choose_from_list question, list
+ ui.choose_from_list question, list
+ end
+
+ ##
+ # Displays the given +statement+ on the standard output (or equivalent).
+
+ def say statement = ''
+ ui.say statement
+ end
+
+ ##
+ # Terminates the RubyGems process with the given +exit_code+
+
+ def terminate_interaction exit_code = 0
+ ui.terminate_interaction exit_code
+ end
+
+ ##
+ # Calls +say+ with +msg+ or the results of the block if really_verbose
+ # is true.
+
+ def verbose msg = nil
+ say(msg || yield) if Gem.configuration.really_verbose
+ end
+end
+
+##
+# Gem::StreamUI implements a simple stream based user interface.
+
+class Gem::StreamUI
+
+ ##
+ # The input stream
+
+ attr_reader :ins
+
+ ##
+ # The output stream
+
+ attr_reader :outs
+
+ ##
+ # The error stream
+
+ attr_reader :errs
+
+ ##
+ # Creates a new StreamUI wrapping +in_stream+ for user input, +out_stream+
+ # for standard output, +err_stream+ for error output. If +usetty+ is true
+ # then special operations (like asking for passwords) will use the TTY
+ # commands to disable character echo.
+
+ def initialize(in_stream, out_stream, err_stream=STDERR, usetty=true)
+ @ins = in_stream
+ @outs = out_stream
+ @errs = err_stream
+ @usetty = usetty
+ end
+
+ ##
+ # Returns true if TTY methods should be used on this StreamUI.
+
+ def tty?
+ if RUBY_VERSION < '1.9.3' and RUBY_PLATFORM =~ /mingw|mswin/ then
+ @usetty
+ else
+ @usetty && @ins.tty?
+ end
+ end
+
+ ##
+ # Prints a formatted backtrace to the errors stream if backtraces are
+ # enabled.
+
+ def backtrace exception
+ return unless Gem.configuration.backtrace
+
+ @errs.puts "\t#{exception.backtrace.join "\n\t"}"
+ end
+
+ ##
+ # Choose from a list of options. +question+ is a prompt displayed above
+ # the list. +list+ is a list of option strings. Returns the pair
+ # [option_name, option_index].
+
+ def choose_from_list(question, list)
+ @outs.puts question
+
+ list.each_with_index do |item, index|
+ @outs.puts " #{index+1}. #{item}"
+ end
+
+ @outs.print "> "
+ @outs.flush
+
+ result = @ins.gets
+
+ return nil, nil unless result
+
+ result = result.strip.to_i - 1
+ return list[result], result
+ end
+
+ ##
+ # Ask a question. Returns a true for yes, false for no. If not connected
+ # to a tty, raises an exception if default is nil, otherwise returns
+ # default.
+
+ def ask_yes_no(question, default=nil)
+ unless tty? then
+ if default.nil? then
+ raise Gem::OperationNotSupportedError,
+ "Not connected to a tty and no default specified"
+ else
+ return default
+ end
+ end
+
+ default_answer = case default
+ when nil
+ 'yn'
+ when true
+ 'Yn'
+ else
+ 'yN'
+ end
+
+ result = nil
+
+ while result.nil? do
+ result = case ask "#{question} [#{default_answer}]"
+ when /^y/i then true
+ when /^n/i then false
+ when /^$/ then default
+ else nil
+ end
+ end
+
+ return result
+ end
+
+ ##
+ # Ask a question. Returns an answer if connected to a tty, nil otherwise.
+
+ def ask(question)
+ return nil if not tty?
+
+ @outs.print(question + " ")
+ @outs.flush
+
+ result = @ins.gets
+ result.chomp! if result
+ result
+ end
+
+ ##
+ # Ask for a password. Does not echo response to terminal.
+
+ def ask_for_password(question)
+ return nil if not tty?
+
+ @outs.print(question, " ")
+ @outs.flush
+
+ password = _gets_noecho
+ @outs.puts
+ password.chomp! if password
+ password
+ end
+
+ if IO.method_defined?(:noecho) then
+ def _gets_noecho
+ @ins.noecho {@ins.gets}
+ end
+ elsif Gem.win_platform?
+ def _gets_noecho
+ require "Win32API"
+ password = ''
+
+ while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
+ break if char == 10 || char == 13 # received carriage return or newline
+ if char == 127 || char == 8 # backspace and delete
+ password.slice!(-1, 1)
+ else
+ password << char.chr
+ end
+ end
+ password
+ end
+ else
+ def _gets_noecho
+ system "stty -echo"
+ begin
+ @ins.gets
+ ensure
+ system "stty echo"
+ end
+ end
+ end
+
+ ##
+ # Display a statement.
+
+ def say(statement="")
+ @outs.puts statement
+ end
+
+ ##
+ # Display an informational alert. Will ask +question+ if it is not nil.
+
+ def alert(statement, question=nil)
+ @outs.puts "INFO: #{statement}"
+ ask(question) if question
+ end
+
+ ##
+ # Display a warning on stderr. Will ask +question+ if it is not nil.
+
+ def alert_warning(statement, question=nil)
+ @errs.puts "WARNING: #{statement}"
+ ask(question) if question
+ end
+
+ ##
+ # Display an error message in a location expected to get error messages.
+ # Will ask +question+ if it is not nil.
+
+ def alert_error(statement, question=nil)
+ @errs.puts "ERROR: #{statement}"
+ ask(question) if question
+ end
+
+ ##
+ # Display a debug message on the same location as error messages.
+
+ def debug(statement)
+ @errs.puts statement
+ end
+
+ ##
+ # Terminate the application with exit code +status+, running any exit
+ # handlers that might have been defined.
+
+ def terminate_interaction(status = 0)
+ close
+ raise Gem::SystemExitException, status
+ end
+
+ def close
+ end
+
+ ##
+ # Return a progress reporter object chosen from the current verbosity.
+
+ def progress_reporter(*args)
+ if self.kind_of?(Gem::SilentUI)
+ return SilentProgressReporter.new(@outs, *args)
+ end
+
+ case Gem.configuration.verbose
+ when nil, false
+ SilentProgressReporter.new(@outs, *args)
+ when true
+ SimpleProgressReporter.new(@outs, *args)
+ else
+ VerboseProgressReporter.new(@outs, *args)
+ end
+ end
+
+ ##
+ # An absolutely silent progress reporter.
+
+ class SilentProgressReporter
+
+ ##
+ # The count of items is never updated for the silent progress reporter.
+
+ attr_reader :count
+
+ ##
+ # Creates a silent progress reporter that ignores all input arguments.
+
+ def initialize(out_stream, size, initial_message, terminal_message = nil)
+ end
+
+ ##
+ # Does not print +message+ when updated as this object has taken a vow of
+ # silence.
+
+ def updated(message)
+ end
+
+ ##
+ # Does not print anything when complete as this object has taken a vow of
+ # silence.
+
+ def done
+ end
+ end
+
+ ##
+ # A basic dotted progress reporter.
+
+ class SimpleProgressReporter
+
+ include Gem::DefaultUserInteraction
+
+ ##
+ # The number of progress items counted so far.
+
+ attr_reader :count
+
+ ##
+ # Creates a new progress reporter that will write to +out_stream+ for
+ # +size+ items. Shows the given +initial_message+ when progress starts
+ # and the +terminal_message+ when it is complete.
+
+ def initialize(out_stream, size, initial_message,
+ terminal_message = "complete")
+ @out = out_stream
+ @total = size
+ @count = 0
+ @terminal_message = terminal_message
+
+ @out.puts initial_message
+ end
+
+ ##
+ # Prints out a dot and ignores +message+.
+
+ def updated(message)
+ @count += 1
+ @out.print "."
+ @out.flush
+ end
+
+ ##
+ # Prints out the terminal message.
+
+ def done
+ @out.puts "\n#{@terminal_message}"
+ end
+
+ end
+
+ ##
+ # A progress reporter that prints out messages about the current progress.
+
+ class VerboseProgressReporter
+
+ include Gem::DefaultUserInteraction
+
+ ##
+ # The number of progress items counted so far.
+
+ attr_reader :count
+
+ ##
+ # Creates a new progress reporter that will write to +out_stream+ for
+ # +size+ items. Shows the given +initial_message+ when progress starts
+ # and the +terminal_message+ when it is complete.
+
+ def initialize(out_stream, size, initial_message,
+ terminal_message = 'complete')
+ @out = out_stream
+ @total = size
+ @count = 0
+ @terminal_message = terminal_message
+
+ @out.puts initial_message
+ end
+
+ ##
+ # Prints out the position relative to the total and the +message+.
+
+ def updated(message)
+ @count += 1
+ @out.puts "#{@count}/#{@total}: #{message}"
+ end
+
+ ##
+ # Prints out the terminal message.
+
+ def done
+ @out.puts @terminal_message
+ end
+ end
+
+ ##
+ # Return a download reporter object chosen from the current verbosity
+
+ def download_reporter(*args)
+ if self.kind_of?(Gem::SilentUI)
+ return SilentDownloadReporter.new(@outs, *args)
+ end
+
+ case Gem.configuration.verbose
+ when nil, false
+ SilentDownloadReporter.new(@outs, *args)
+ else
+ VerboseDownloadReporter.new(@outs, *args)
+ end
+ end
+
+ ##
+ # An absolutely silent download reporter.
+
+ class SilentDownloadReporter
+
+ ##
+ # The silent download reporter ignores all arguments
+
+ def initialize(out_stream, *args)
+ end
+
+ ##
+ # The silent download reporter does not display +filename+ or care about
+ # +filesize+ because it is silent.
+
+ def fetch(filename, filesize)
+ end
+
+ ##
+ # Nothing can update the silent download reporter.
+
+ def update(current)
+ end
+
+ ##
+ # The silent download reporter won't tell you when the download is done.
+ # Because it is silent.
+
+ def done
+ end
+ end
+
+ ##
+ # A progress reporter that prints out messages about the current progress.
+
+ class VerboseDownloadReporter
+
+ ##
+ # The current file name being displayed
+
+ attr_reader :file_name
+
+ ##
+ # The total bytes in the file
+
+ attr_reader :total_bytes
+
+ ##
+ # The current progress (0 to 100)
+
+ attr_reader :progress
+
+ ##
+ # Creates a new verbose download reporter that will display on
+ # +out_stream+. The other arguments are ignored.
+
+ def initialize(out_stream, *args)
+ @out = out_stream
+ @progress = 0
+ end
+
+ ##
+ # Tells the download reporter that the +file_name+ is being fetched and
+ # contains +total_bytes+.
+
+ def fetch(file_name, total_bytes)
+ @file_name = file_name
+ @total_bytes = total_bytes.to_i
+ @units = @total_bytes.zero? ? 'B' : '%'
+
+ update_display(false)
+ end
+
+ ##
+ # Updates the verbose download reporter for the given number of +bytes+.
+
+ def update(bytes)
+ new_progress = if @units == 'B' then
+ bytes
+ else
+ ((bytes.to_f * 100) / total_bytes.to_f).ceil
+ end
+
+ return if new_progress == @progress
+
+ @progress = new_progress
+ update_display
+ end
+
+ ##
+ # Indicates the download is complete.
+
+ def done
+ @progress = 100 if @units == '%'
+ update_display(true, true)
+ end
+
+ private
+
+ def update_display(show_progress = true, new_line = false) # :nodoc:
+ return unless @out.tty?
+
+ if show_progress then
+ @out.print "\rFetching: %s (%3d%s)" % [@file_name, @progress, @units]
+ else
+ @out.print "Fetching: %s" % @file_name
+ end
+ @out.puts if new_line
+ end
+ end
+end
+
+##
+# Subclass of StreamUI that instantiates the user interaction using STDIN,
+# STDOUT, and STDERR.
+
+class Gem::ConsoleUI < Gem::StreamUI
+
+ ##
+ # The Console UI has no arguments as it defaults to reading input from
+ # stdin, output to stdout and warnings or errors to stderr.
+
+ def initialize
+ super STDIN, STDOUT, STDERR, true
+ end
+end
+
+##
+# SilentUI is a UI choice that is absolutely silent.
+
+class Gem::SilentUI < Gem::StreamUI
+
+ ##
+ # The SilentUI has no arguments as it does not use any stream.
+
+ def initialize
+ reader, writer = nil, nil
+
+ begin
+ reader = File.open('/dev/null', 'r')
+ writer = File.open('/dev/null', 'w')
+ rescue Errno::ENOENT
+ reader = File.open('nul', 'r')
+ writer = File.open('nul', 'w')
+ end
+
+ super reader, writer, writer, false
+ end
+
+ def close
+ super
+ @ins.close
+ @outs.close
+ end
+
+ def download_reporter(*args) # :nodoc:
+ SilentDownloadReporter.new(@outs, *args)
+ end
+
+ def progress_reporter(*args) # :nodoc:
+ SilentProgressReporter.new(@outs, *args)
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/util.rb b/jni/ruby/lib/rubygems/util.rb
new file mode 100644
index 0000000..cd0af4d
--- /dev/null
+++ b/jni/ruby/lib/rubygems/util.rb
@@ -0,0 +1,134 @@
+##
+# This module contains various utility methods as module methods.
+
+module Gem::Util
+
+ @silent_mutex = nil
+
+ ##
+ # Zlib::GzipReader wrapper that unzips +data+.
+
+ def self.gunzip(data)
+ require 'zlib'
+ require 'rubygems/util/stringio'
+ data = Gem::StringSource.new data
+
+ unzipped = Zlib::GzipReader.new(data).read
+ unzipped.force_encoding Encoding::BINARY if Object.const_defined? :Encoding
+ unzipped
+ end
+
+ ##
+ # Zlib::GzipWriter wrapper that zips +data+.
+
+ def self.gzip(data)
+ require 'zlib'
+ require 'rubygems/util/stringio'
+ zipped = Gem::StringSink.new
+ zipped.set_encoding Encoding::BINARY if Object.const_defined? :Encoding
+
+ Zlib::GzipWriter.wrap zipped do |io| io.write data end
+
+ zipped.string
+ end
+
+ ##
+ # A Zlib::Inflate#inflate wrapper
+
+ def self.inflate(data)
+ require 'zlib'
+ Zlib::Inflate.inflate data
+ end
+
+ ##
+ # This calls IO.popen where it accepts an array for a +command+ (Ruby 1.9+)
+ # and implements an IO.popen-like behavior where it does not accept an array
+ # for a command.
+
+ def self.popen *command
+ IO.popen command, &:read
+ rescue TypeError # ruby 1.8 only supports string command
+ r, w = IO.pipe
+
+ pid = fork do
+ STDIN.close
+ STDOUT.reopen w
+
+ exec(*command)
+ end
+
+ w.close
+
+ begin
+ return r.read
+ ensure
+ Process.wait pid
+ end
+ end
+
+ NULL_DEVICE = defined?(IO::NULL) ? IO::NULL : Gem.win_platform? ? 'NUL' : '/dev/null'
+
+ ##
+ # Invokes system, but silences all output.
+
+ def self.silent_system *command
+ opt = {:out => NULL_DEVICE, :err => [:child, :out]}
+ if Hash === command.last
+ opt.update(command.last)
+ cmds = command[0...-1]
+ else
+ cmds = command.dup
+ end
+ return system(*(cmds << opt))
+ rescue TypeError
+ require 'thread'
+
+ @silent_mutex ||= Mutex.new
+
+ null_device = NULL_DEVICE
+
+ @silent_mutex.synchronize do
+ begin
+ stdout = STDOUT.dup
+ stderr = STDERR.dup
+
+ STDOUT.reopen null_device, 'w'
+ STDERR.reopen null_device, 'w'
+
+ return system(*command)
+ ensure
+ STDOUT.reopen stdout
+ STDERR.reopen stderr
+ stdout.close
+ stderr.close
+ end
+ end
+ end
+
+ ##
+ # Enumerates the parents of +directory+.
+
+ def self.traverse_parents directory
+ return enum_for __method__, directory unless block_given?
+
+ here = File.expand_path directory
+ start = here
+
+ Dir.chdir start
+
+ begin
+ loop do
+ yield here
+
+ Dir.chdir '..'
+
+ return if Dir.pwd == here # toplevel
+
+ here = Dir.pwd
+ end
+ ensure
+ Dir.chdir start
+ end
+ end
+
+end
diff --git a/jni/ruby/lib/rubygems/util/list.rb b/jni/ruby/lib/rubygems/util/list.rb
new file mode 100644
index 0000000..9bc11fe
--- /dev/null
+++ b/jni/ruby/lib/rubygems/util/list.rb
@@ -0,0 +1,48 @@
+module Gem
+ List = Struct.new(:value, :tail)
+
+ class List
+ def each
+ n = self
+ while n
+ yield n.value
+ n = n.tail
+ end
+ end
+
+ def to_a
+ ary = []
+ n = self
+ while n
+ ary.unshift n.value
+ n = n.tail
+ end
+
+ ary
+ end
+
+ def find
+ n = self
+ while n
+ v = n.value
+ return v if yield(v)
+ n = n.tail
+ end
+
+ nil
+ end
+
+ def prepend(value)
+ List.new value, self
+ end
+
+ def pretty_print q # :nodoc:
+ q.pp to_a
+ end
+
+ def self.prepend(list, value)
+ return List.new(value) unless list
+ List.new value, list
+ end
+ end
+end
diff --git a/jni/ruby/lib/rubygems/util/stringio.rb b/jni/ruby/lib/rubygems/util/stringio.rb
new file mode 100644
index 0000000..2ea6961
--- /dev/null
+++ b/jni/ruby/lib/rubygems/util/stringio.rb
@@ -0,0 +1,34 @@
+class Gem::StringSink
+ def initialize
+ @string = ""
+ end
+
+ attr_reader :string
+
+ def write(s)
+ @string += s
+ s.size
+ end
+
+ def set_encoding(enc)
+ @string.force_encoding enc
+ end
+end
+
+class Gem::StringSource
+ def initialize(str)
+ @string = str.dup
+ end
+
+ def read(count=nil)
+ if count
+ @string.slice!(0,count)
+ else
+ s = @string
+ @string = ""
+ s
+ end
+ end
+
+ alias_method :readpartial, :read
+end
diff --git a/jni/ruby/lib/rubygems/validator.rb b/jni/ruby/lib/rubygems/validator.rb
new file mode 100644
index 0000000..6992af1
--- /dev/null
+++ b/jni/ruby/lib/rubygems/validator.rb
@@ -0,0 +1,165 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems/package'
+require 'rubygems/installer'
+
+##
+# Validator performs various gem file and gem database validation
+
+class Gem::Validator
+
+ include Gem::UserInteraction
+
+ def initialize # :nodoc:
+ require 'find'
+ end
+
+ ##
+ # Given a gem file's contents, validates against its own MD5 checksum
+ # gem_data:: [String] Contents of the gem file
+
+ def verify_gem(gem_data)
+ # TODO remove me? The code here only validate an MD5SUM that was
+ # in some old formatted gems, but hasn't been for a long time.
+ end
+
+ ##
+ # Given the path to a gem file, validates against its own MD5 checksum
+ #
+ # gem_path:: [String] Path to gem file
+
+ def verify_gem_file(gem_path)
+ open gem_path, Gem.binary_mode do |file|
+ gem_data = file.read
+ verify_gem gem_data
+ end
+ rescue Errno::ENOENT, Errno::EINVAL
+ raise Gem::VerificationError, "missing gem file #{gem_path}"
+ end
+
+ private
+
+ def find_files_for_gem(gem_directory)
+ installed_files = []
+
+ Find.find gem_directory do |file_name|
+ fn = file_name[gem_directory.size..file_name.size-1].sub(/^\//, "")
+ installed_files << fn unless
+ fn =~ /CVS/ || fn.empty? || File.directory?(file_name)
+ end
+
+ installed_files
+ end
+
+ public
+
+ ##
+ # Describes a problem with a file in a gem.
+
+ ErrorData = Struct.new :path, :problem do
+ def <=> other # :nodoc:
+ return nil unless self.class === other
+
+ [path, problem] <=> [other.path, other.problem]
+ end
+ end
+
+ ##
+ # Checks the gem directory for the following potential
+ # inconsistencies/problems:
+ #
+ # * Checksum gem itself
+ # * For each file in each gem, check consistency of installed versions
+ # * Check for files that aren't part of the gem but are in the gems directory
+ # * 1 cache - 1 spec - 1 directory.
+ #
+ # returns a hash of ErrorData objects, keyed on the problem gem's name.
+ #--
+ # TODO needs further cleanup
+
+ def alien(gems=[])
+ errors = Hash.new { |h,k| h[k] = {} }
+
+ Gem::Specification.each do |spec|
+ next unless gems.include? spec.name unless gems.empty?
+ next if spec.default_gem?
+
+ gem_name = spec.file_name
+ gem_path = spec.cache_file
+ spec_path = spec.spec_file
+ gem_directory = spec.full_gem_path
+
+ unless File.directory? gem_directory then
+ errors[gem_name][spec.full_name] =
+ "Gem registered but doesn't exist at #{gem_directory}"
+ next
+ end
+
+ unless File.exist? spec_path then
+ errors[gem_name][spec_path] = "Spec file missing for installed gem"
+ end
+
+ begin
+ verify_gem_file(gem_path)
+
+ good, gone, unreadable = nil, nil, nil, nil
+
+ open gem_path, Gem.binary_mode do |file|
+ package = Gem::Package.new gem_path
+
+ good, gone = package.contents.partition { |file_name|
+ File.exist? File.join(gem_directory, file_name)
+ }
+
+ gone.sort.each do |path|
+ errors[gem_name][path] = "Missing file"
+ end
+
+ good, unreadable = good.partition { |file_name|
+ File.readable? File.join(gem_directory, file_name)
+ }
+
+ unreadable.sort.each do |path|
+ errors[gem_name][path] = "Unreadable file"
+ end
+
+ good.each do |entry, data|
+ begin
+ next unless data # HACK `gem check -a mkrf`
+
+ source = File.join gem_directory, entry['path']
+
+ open source, Gem.binary_mode do |f|
+ unless f.read == data then
+ errors[gem_name][entry['path']] = "Modified from original"
+ end
+ end
+ end
+ end
+ end
+
+ installed_files = find_files_for_gem(gem_directory)
+ extras = installed_files - good - unreadable
+
+ extras.each do |extra|
+ errors[gem_name][extra] = "Extra file"
+ end
+ rescue Gem::VerificationError => e
+ errors[gem_name][gem_path] = e.message
+ end
+ end
+
+ errors.each do |name, subhash|
+ errors[name] = subhash.map do |path, msg|
+ ErrorData.new path, msg
+ end.sort
+ end
+
+ errors
+ end
+end
+
diff --git a/jni/ruby/lib/rubygems/version.rb b/jni/ruby/lib/rubygems/version.rb
new file mode 100644
index 0000000..8335ebe
--- /dev/null
+++ b/jni/ruby/lib/rubygems/version.rb
@@ -0,0 +1,356 @@
+##
+# The Version class processes string versions into comparable
+# values. A version string should normally be a series of numbers
+# separated by periods. Each part (digits separated by periods) is
+# considered its own number, and these are used for sorting. So for
+# instance, 3.10 sorts higher than 3.2 because ten is greater than
+# two.
+#
+# If any part contains letters (currently only a-z are supported) then
+# that version is considered prerelease. Versions with a prerelease
+# part in the Nth part sort less than versions with N-1
+# parts. Prerelease parts are sorted alphabetically using the normal
+# Ruby string sorting rules. If a prerelease part contains both
+# letters and numbers, it will be broken into multiple parts to
+# provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is
+# greater than 1.0.a9).
+#
+# Prereleases sort between real releases (newest to oldest):
+#
+# 1. 1.0
+# 2. 1.0.b1
+# 3. 1.0.a.2
+# 4. 0.9
+#
+# If you want to specify a version restriction that includes both prereleases
+# and regular releases of the 1.x series this is the best way:
+#
+# s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
+#
+# == How Software Changes
+#
+# Users expect to be able to specify a version constraint that gives them
+# some reasonable expectation that new versions of a library will work with
+# their software if the version constraint is true, and not work with their
+# software if the version constraint is false. In other words, the perfect
+# system will accept all compatible versions of the library and reject all
+# incompatible versions.
+#
+# Libraries change in 3 ways (well, more than 3, but stay focused here!).
+#
+# 1. The change may be an implementation detail only and have no effect on
+# the client software.
+# 2. The change may add new features, but do so in a way that client software
+# written to an earlier version is still compatible.
+# 3. The change may change the public interface of the library in such a way
+# that old software is no longer compatible.
+#
+# Some examples are appropriate at this point. Suppose I have a Stack class
+# that supports a <tt>push</tt> and a <tt>pop</tt> method.
+#
+# === Examples of Category 1 changes:
+#
+# * Switch from an array based implementation to a linked-list based
+# implementation.
+# * Provide an automatic (and transparent) backing store for large stacks.
+#
+# === Examples of Category 2 changes might be:
+#
+# * Add a <tt>depth</tt> method to return the current depth of the stack.
+# * Add a <tt>top</tt> method that returns the current top of stack (without
+# changing the stack).
+# * Change <tt>push</tt> so that it returns the item pushed (previously it
+# had no usable return value).
+#
+# === Examples of Category 3 changes might be:
+#
+# * Changes <tt>pop</tt> so that it no longer returns a value (you must use
+# <tt>top</tt> to get the top of the stack).
+# * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>.
+#
+# == RubyGems Rational Versioning
+#
+# * Versions shall be represented by three non-negative integers, separated
+# by periods (e.g. 3.1.4). The first integers is the "major" version
+# number, the second integer is the "minor" version number, and the third
+# integer is the "build" number.
+#
+# * A category 1 change (implementation detail) will increment the build
+# number.
+#
+# * A category 2 change (backwards compatible) will increment the minor
+# version number and reset the build number.
+#
+# * A category 3 change (incompatible) will increment the major build number
+# and reset the minor and build numbers.
+#
+# * Any "public" release of a gem should have a different version. Normally
+# that means incrementing the build number. This means a developer can
+# generate builds all day long, but as soon as they make a public release,
+# the version must be updated.
+#
+# === Examples
+#
+# Let's work through a project lifecycle using our Stack example from above.
+#
+# Version 0.0.1:: The initial Stack class is release.
+# Version 0.0.2:: Switched to a linked=list implementation because it is
+# cooler.
+# Version 0.1.0:: Added a <tt>depth</tt> method.
+# Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil
+# (<tt>pop</tt> used to return the old top item).
+# Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it
+# return nil).
+# Version 1.1.1:: Fixed a bug in the linked list implementation.
+# Version 1.1.2:: Fixed a bug introduced in the last fix.
+#
+# Client A needs a stack with basic push/pop capability. They write to the
+# original interface (no <tt>top</tt>), so their version constraint looks like:
+#
+# gem 'stack', '~> 0.0'
+#
+# Essentially, any version is OK with Client A. An incompatible change to
+# the library will cause them grief, but they are willing to take the chance
+# (we call Client A optimistic).
+#
+# Client B is just like Client A except for two things: (1) They use the
+# <tt>depth</tt> method and (2) they are worried about future
+# incompatibilities, so they write their version constraint like this:
+#
+# gem 'stack', '~> 0.1'
+#
+# The <tt>depth</tt> method was introduced in version 0.1.0, so that version
+# or anything later is fine, as long as the version stays below version 1.0
+# where incompatibilities are introduced. We call Client B pessimistic
+# because they are worried about incompatible future changes (it is OK to be
+# pessimistic!).
+#
+# == Preventing Version Catastrophe:
+#
+# From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
+#
+# Let's say you're depending on the fnord gem version 2.y.z. If you
+# specify your dependency as ">= 2.0.0" then, you're good, right? What
+# happens if fnord 3.0 comes out and it isn't backwards compatible
+# with 2.y.z? Your stuff will break as a result of using ">=". The
+# better route is to specify your dependency with an "approximate" version
+# specifier ("~>"). They're a tad confusing, so here is how the dependency
+# specifiers work:
+#
+# Specification From ... To (exclusive)
+# ">= 3.0" 3.0 ... &infin;
+# "~> 3.0" 3.0 ... 4.0
+# "~> 3.0.0" 3.0.0 ... 3.1
+# "~> 3.5" 3.5 ... 4.0
+# "~> 3.5.0" 3.5.0 ... 3.6
+# "~> 3" 3.0 ... 4.0
+#
+# For the last example, single-digit versions are automatically extended with
+# a zero to give a sensible result.
+
+class Gem::Version
+ autoload :Requirement, 'rubygems/requirement'
+
+ include Comparable
+
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
+
+ ##
+ # A string representation of this Version.
+
+ def version
+ @version.dup
+ end
+
+ alias to_s version
+
+ ##
+ # True if the +version+ string matches RubyGems' requirements.
+
+ def self.correct? version
+ version.to_s =~ ANCHORED_VERSION_PATTERN
+ end
+
+ ##
+ # Factory method to create a Version object. Input may be a Version
+ # or a String. Intended to simplify client code.
+ #
+ # ver1 = Version.create('1.3.17') # -> (Version object)
+ # ver2 = Version.create(ver1) # -> (ver1)
+ # ver3 = Version.create(nil) # -> nil
+
+ def self.create input
+ if self === input then # check yourself before you wreck yourself
+ input
+ elsif input.nil? then
+ nil
+ else
+ new input
+ end
+ end
+
+ @@all = {}
+
+ def self.new version # :nodoc:
+ return super unless Gem::Version == self
+
+ @@all[version] ||= super
+ end
+
+ ##
+ # Constructs a Version from the +version+ string. A version string is a
+ # series of digits or ASCII letters separated by dots.
+
+ def initialize version
+ raise ArgumentError, "Malformed version number string #{version}" unless
+ self.class.correct?(version)
+
+ @version = version.to_s.strip.gsub("-",".pre.")
+ @segments = nil
+ end
+
+ ##
+ # Return a new version object where the next to the last revision
+ # number is one greater (e.g., 5.3.1 => 5.4).
+ #
+ # Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
+
+ def bump
+ segments = self.segments.dup
+ segments.pop while segments.any? { |s| String === s }
+ segments.pop if segments.size > 1
+
+ segments[-1] = segments[-1].succ
+ self.class.new segments.join(".")
+ end
+
+ ##
+ # A Version is only eql? to another version if it's specified to the
+ # same precision. Version "1.0" is not the same as version "1".
+
+ def eql? other
+ self.class === other and @version == other.version
+ end
+
+ def hash # :nodoc:
+ @hash ||= segments.hash
+ end
+
+ def init_with coder # :nodoc:
+ yaml_initialize coder.tag, coder.map
+ end
+
+ def inspect # :nodoc:
+ "#<#{self.class} #{version.inspect}>"
+ end
+
+ ##
+ # Dump only the raw version string, not the complete object. It's a
+ # string for backwards (RubyGems 1.3.5 and earlier) compatibility.
+
+ def marshal_dump
+ [version]
+ end
+
+ ##
+ # Load custom marshal format. It's a string for backwards (RubyGems
+ # 1.3.5 and earlier) compatibility.
+
+ def marshal_load array
+ initialize array[0]
+ end
+
+ def yaml_initialize(tag, map) # :nodoc:
+ @version = map['version']
+ @segments = nil
+ @hash = nil
+ end
+
+ def to_yaml_properties # :nodoc:
+ ["@version"]
+ end
+
+ def encode_with coder # :nodoc:
+ coder.add 'version', @version
+ end
+
+ ##
+ # A version is considered a prerelease if it contains a letter.
+
+ def prerelease?
+ @prerelease ||= !!(@version =~ /[a-zA-Z]/)
+ end
+
+ def pretty_print q # :nodoc:
+ q.text "Gem::Version.new(#{version.inspect})"
+ end
+
+ ##
+ # The release for this version (e.g. 1.2.0.a -> 1.2.0).
+ # Non-prerelease versions return themselves.
+
+ def release
+ return self unless prerelease?
+
+ segments = self.segments.dup
+ segments.pop while segments.any? { |s| String === s }
+ self.class.new segments.join('.')
+ end
+
+ def segments # :nodoc:
+
+ # segments is lazy so it can pick up version values that come from
+ # old marshaled versions, which don't go through marshal_load.
+
+ @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
+ /^\d+$/ =~ s ? s.to_i : s
+ end
+ end
+
+ ##
+ # A recommended version for use with a ~> Requirement.
+
+ def approximate_recommendation
+ segments = self.segments.dup
+
+ segments.pop while segments.any? { |s| String === s }
+ segments.pop while segments.size > 2
+ segments.push 0 while segments.size < 2
+
+ "~> #{segments.join(".")}"
+ end
+
+ ##
+ # Compares this version with +other+ returning -1, 0, or 1 if the
+ # other version is larger, the same, or smaller than this
+ # one. Attempts to compare to something that's not a
+ # <tt>Gem::Version</tt> return +nil+.
+
+ def <=> other
+ return unless Gem::Version === other
+ return 0 if @version == other.version
+
+ lhsegments = segments
+ rhsegments = other.segments
+
+ lhsize = lhsegments.size
+ rhsize = rhsegments.size
+ limit = (lhsize > rhsize ? lhsize : rhsize) - 1
+
+ i = 0
+
+ while i <= limit
+ lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
+ i += 1
+
+ next if lhs == rhs
+ return -1 if String === lhs && Numeric === rhs
+ return 1 if Numeric === lhs && String === rhs
+
+ return lhs <=> rhs
+ end
+
+ return 0
+ end
+end
diff --git a/jni/ruby/lib/rubygems/version_option.rb b/jni/ruby/lib/rubygems/version_option.rb
new file mode 100644
index 0000000..a0755d5
--- /dev/null
+++ b/jni/ruby/lib/rubygems/version_option.rb
@@ -0,0 +1,71 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems'
+
+##
+# Mixin methods for --version and --platform Gem::Command options.
+
+module Gem::VersionOption
+
+ ##
+ # Add the --platform option to the option parser.
+
+ def add_platform_option(task = command, *wrap)
+ OptionParser.accept Gem::Platform do |value|
+ if value == Gem::Platform::RUBY then
+ value
+ else
+ Gem::Platform.new value
+ end
+ end
+
+ add_option('--platform PLATFORM', Gem::Platform,
+ "Specify the platform of gem to #{task}", *wrap) do
+ |value, options|
+ unless options[:added_platform] then
+ Gem.platforms = [Gem::Platform::RUBY]
+ options[:added_platform] = true
+ end
+
+ Gem.platforms << value unless Gem.platforms.include? value
+ end
+ end
+
+ ##
+ # Add the --prerelease option to the option parser.
+
+ def add_prerelease_option(*wrap)
+ add_option("--[no-]prerelease",
+ "Allow prerelease versions of a gem", *wrap) do |value, options|
+ options[:prerelease] = value
+ options[:explicit_prerelease] = true
+ end
+ end
+
+ ##
+ # Add the --version option to the option parser.
+
+ def add_version_option(task = command, *wrap)
+ OptionParser.accept Gem::Requirement do |value|
+ Gem::Requirement.new(*value.split(/\s*,\s*/))
+ end
+
+ add_option('-v', '--version VERSION', Gem::Requirement,
+ "Specify version of gem to #{task}", *wrap) do
+ |value, options|
+ options[:version] = value
+
+ explicit_prerelease_set = !options[:explicit_prerelease].nil?
+ options[:explicit_prerelease] = false unless explicit_prerelease_set
+
+ options[:prerelease] = value.prerelease? unless
+ options[:explicit_prerelease]
+ end
+ end
+
+end
+