rubygems-update 1.8.30 → 2.0.0.preview2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- checksums.yaml +6 -6
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +3 -0
- data/.autotest +6 -3
- data/History.txt +137 -63
- data/LICENSE.txt +1 -5
- data/Manifest.txt +69 -32
- data/README.rdoc +11 -9
- data/Rakefile +24 -38
- data/bin/gem +0 -9
- data/bin/update_rubygems +1 -0
- data/lib/rubygems.rb +193 -405
- data/lib/rubygems/available_set.rb +95 -0
- data/lib/rubygems/command.rb +88 -45
- data/lib/rubygems/command_manager.rb +67 -40
- data/lib/rubygems/commands/build_command.rb +5 -23
- data/lib/rubygems/commands/cert_command.rb +199 -57
- data/lib/rubygems/commands/check_command.rb +14 -39
- data/lib/rubygems/commands/cleanup_command.rb +9 -1
- data/lib/rubygems/commands/contents_command.rb +30 -12
- data/lib/rubygems/commands/dependency_command.rb +3 -8
- data/lib/rubygems/commands/environment_command.rb +13 -8
- data/lib/rubygems/commands/fetch_command.rb +3 -16
- data/lib/rubygems/commands/generate_index_command.rb +7 -47
- data/lib/rubygems/commands/help_command.rb +1 -1
- data/lib/rubygems/commands/install_command.rb +69 -36
- data/lib/rubygems/commands/list_command.rb +6 -4
- data/lib/rubygems/commands/lock_command.rb +1 -1
- data/lib/rubygems/commands/mirror_command.rb +17 -0
- data/lib/rubygems/commands/outdated_command.rb +6 -3
- data/lib/rubygems/commands/owner_command.rb +13 -5
- data/lib/rubygems/commands/pristine_command.rb +19 -4
- data/lib/rubygems/commands/push_command.rb +12 -1
- data/lib/rubygems/commands/query_command.rb +43 -27
- data/lib/rubygems/commands/rdoc_command.rb +23 -28
- data/lib/rubygems/commands/search_command.rb +4 -18
- data/lib/rubygems/commands/server_command.rb +1 -1
- data/lib/rubygems/commands/setup_command.rb +124 -38
- data/lib/rubygems/commands/sources_command.rb +16 -16
- data/lib/rubygems/commands/specification_command.rb +11 -13
- data/lib/rubygems/commands/uninstall_command.rb +24 -7
- data/lib/rubygems/commands/unpack_command.rb +7 -3
- data/lib/rubygems/commands/update_command.rb +22 -36
- data/lib/rubygems/commands/yank_command.rb +98 -0
- data/lib/rubygems/compatibility.rb +51 -0
- data/lib/rubygems/config_file.rb +82 -54
- data/lib/rubygems/core_ext/kernel_gem.rb +53 -0
- data/lib/rubygems/core_ext/kernel_require.rb +119 -0
- data/lib/rubygems/defaults.rb +10 -21
- data/lib/rubygems/dependency.rb +61 -10
- data/lib/rubygems/dependency_installer.rb +157 -69
- data/lib/rubygems/dependency_list.rb +11 -19
- data/lib/rubygems/dependency_resolver.rb +562 -0
- data/lib/rubygems/deprecate.rb +40 -40
- data/lib/rubygems/errors.rb +77 -24
- data/lib/rubygems/exceptions.rb +25 -7
- data/lib/rubygems/ext/builder.rb +20 -23
- data/lib/rubygems/ext/configure_builder.rb +2 -2
- data/lib/rubygems/ext/ext_conf_builder.rb +5 -45
- data/lib/rubygems/ext/rake_builder.rb +2 -2
- data/lib/rubygems/gem_runner.rb +3 -16
- data/lib/rubygems/gemcutter_utilities.rb +22 -7
- data/lib/rubygems/indexer.rb +6 -159
- data/lib/rubygems/install_message.rb +12 -0
- data/lib/rubygems/install_update_options.rb +56 -18
- data/lib/rubygems/installer.rb +244 -134
- data/lib/rubygems/installer_test_case.rb +71 -19
- data/lib/rubygems/mock_gem_ui.rb +17 -0
- data/lib/rubygems/name_tuple.rb +110 -0
- data/lib/rubygems/package.rb +514 -43
- data/lib/rubygems/package/digest_io.rb +64 -0
- data/lib/rubygems/package/old.rb +147 -0
- data/lib/rubygems/package/tar_header.rb +18 -55
- data/lib/rubygems/package/tar_reader.rb +20 -3
- data/lib/rubygems/package/tar_writer.rb +63 -7
- data/lib/rubygems/package_task.rb +3 -4
- data/lib/rubygems/path_support.rb +14 -7
- data/lib/rubygems/platform.rb +19 -26
- data/lib/rubygems/rdoc.rb +316 -0
- data/lib/rubygems/remote_fetcher.rb +117 -54
- data/lib/rubygems/request_set.rb +182 -0
- data/lib/rubygems/requirement.rb +63 -26
- data/lib/rubygems/security.rb +295 -555
- data/lib/rubygems/security/policies.rb +115 -0
- data/lib/rubygems/security/policy.rb +227 -0
- data/lib/rubygems/security/signer.rb +136 -0
- data/lib/rubygems/security/trust_dir.rb +104 -0
- data/lib/rubygems/server.rb +45 -55
- data/lib/rubygems/source.rb +144 -0
- data/lib/rubygems/source_list.rb +87 -0
- data/lib/rubygems/source_local.rb +92 -0
- data/lib/rubygems/source_specific_file.rb +28 -0
- data/lib/rubygems/spec_fetcher.rb +116 -184
- data/lib/rubygems/specification.rb +731 -335
- data/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem +88 -30
- data/lib/rubygems/ssl_certs/Entrust_net-Secure-Server-Certification-Authority.pem +90 -0
- data/lib/rubygems/ssl_certs/VerisignClass3PublicPrimaryCertificationAuthority-G2.pem +57 -0
- data/lib/rubygems/syck_hack.rb +2 -0
- data/lib/rubygems/test_case.rb +199 -109
- data/lib/rubygems/test_utilities.rb +25 -5
- data/lib/rubygems/uninstaller.rb +62 -20
- data/lib/rubygems/user_interaction.rb +10 -0
- data/lib/rubygems/validator.rb +33 -40
- data/lib/rubygems/version.rb +19 -8
- data/setup.rb +8 -1
- data/test/rubygems/alternate_cert.pem +9 -0
- data/test/rubygems/alternate_cert_32.pem +9 -0
- data/test/rubygems/alternate_key.pem +9 -0
- data/test/rubygems/bad_rake.rb +1 -0
- data/test/rubygems/child_cert.pem +9 -0
- data/test/rubygems/child_cert_32.pem +9 -0
- data/test/rubygems/child_key.pem +9 -0
- data/test/rubygems/data/null-type.gemspec.rz +0 -0
- data/test/rubygems/expired_cert.pem +9 -0
- data/test/rubygems/future_cert.pem +9 -0
- data/test/rubygems/future_cert_32.pem +9 -0
- data/test/rubygems/good_rake.rb +1 -0
- data/test/rubygems/grandchild_cert.pem +9 -0
- data/test/rubygems/grandchild_cert_32.pem +9 -0
- data/test/rubygems/grandchild_key.pem +9 -0
- data/test/rubygems/invalid_issuer_cert.pem +9 -0
- data/test/rubygems/invalid_issuer_cert_32.pem +9 -0
- data/test/rubygems/invalid_key.pem +9 -0
- data/test/rubygems/invalid_signer_cert.pem +9 -0
- data/test/rubygems/invalid_signer_cert_32.pem +9 -0
- data/test/rubygems/invalidchild_cert.pem +9 -0
- data/test/rubygems/invalidchild_cert_32.pem +9 -0
- data/test/rubygems/invalidchild_key.pem +9 -0
- data/test/rubygems/plugin/exception/rubygems_plugin.rb +1 -1
- data/test/rubygems/plugin/standarderror/rubygems_plugin.rb +1 -1
- data/test/rubygems/private_key.pem +7 -25
- data/test/rubygems/public_cert.pem +8 -18
- data/test/rubygems/public_cert_32.pem +10 -0
- data/test/rubygems/public_key.pem +4 -0
- data/test/rubygems/rubygems/commands/crash_command.rb +1 -1
- data/test/rubygems/test_config.rb +4 -6
- data/test/rubygems/test_deprecate.rb +76 -0
- data/test/rubygems/test_gem.rb +318 -83
- data/test/rubygems/test_gem_available_set.rb +106 -0
- data/test/rubygems/test_gem_command.rb +10 -0
- data/test/rubygems/test_gem_command_manager.rb +55 -9
- data/test/rubygems/test_gem_commands_build_command.rb +11 -19
- data/test/rubygems/test_gem_commands_cert_command.rb +441 -42
- data/test/rubygems/test_gem_commands_cleanup_command.rb +29 -1
- data/test/rubygems/test_gem_commands_contents_command.rb +23 -0
- data/test/rubygems/test_gem_commands_dependency_command.rb +5 -0
- data/test/rubygems/test_gem_commands_fetch_command.rb +19 -20
- data/test/rubygems/test_gem_commands_generate_index_command.rb +2 -83
- data/test/rubygems/test_gem_commands_help_command.rb +2 -1
- data/test/rubygems/test_gem_commands_install_command.rb +647 -48
- data/test/rubygems/test_gem_commands_mirror.rb +32 -0
- data/test/rubygems/test_gem_commands_owner_command.rb +4 -8
- data/test/rubygems/test_gem_commands_pristine_command.rb +99 -4
- data/test/rubygems/test_gem_commands_push_command.rb +62 -8
- data/test/rubygems/test_gem_commands_query_command.rb +51 -0
- data/test/rubygems/test_gem_commands_search_command.rb +25 -0
- data/test/rubygems/test_gem_commands_setup_command.rb +45 -0
- data/test/rubygems/test_gem_commands_sources_command.rb +21 -6
- data/test/rubygems/test_gem_commands_specification_command.rb +33 -1
- data/test/rubygems/test_gem_commands_uninstall_command.rb +91 -31
- data/test/rubygems/test_gem_commands_unpack_command.rb +3 -3
- data/test/rubygems/test_gem_commands_update_command.rb +56 -38
- data/test/rubygems/test_gem_commands_which_command.rb +4 -4
- data/test/rubygems/test_gem_commands_yank_command.rb +97 -0
- data/test/rubygems/test_gem_config_file.rb +66 -21
- data/test/rubygems/test_gem_dependency.rb +46 -0
- data/test/rubygems/test_gem_dependency_installer.rb +228 -18
- data/test/rubygems/test_gem_dependency_list.rb +0 -9
- data/test/rubygems/test_gem_dependency_resolver.rb +327 -0
- data/test/rubygems/test_gem_ext_configure_builder.rb +4 -4
- data/test/rubygems/test_gem_ext_ext_conf_builder.rb +21 -49
- data/test/rubygems/test_gem_ext_rake_builder.rb +13 -13
- data/test/rubygems/test_gem_gem_runner.rb +27 -5
- data/test/rubygems/test_gem_gemcutter_utilities.rb +19 -0
- data/test/rubygems/test_gem_indexer.rb +14 -227
- data/test/rubygems/test_gem_install_update_options.rb +83 -3
- data/test/rubygems/test_gem_installer.rb +211 -236
- data/test/rubygems/test_gem_local_remote_options.rb +8 -2
- data/test/rubygems/test_gem_name_tuple.rb +15 -0
- data/test/rubygems/test_gem_package.rb +547 -0
- data/test/rubygems/test_gem_package_old.rb +37 -0
- data/test/rubygems/test_gem_package_tar_reader.rb +32 -0
- data/test/rubygems/test_gem_package_tar_writer.rb +84 -1
- data/test/rubygems/test_gem_path_support.rb +4 -30
- data/test/rubygems/test_gem_platform.rb +3 -6
- data/test/rubygems/test_gem_rdoc.rb +245 -0
- data/test/rubygems/test_gem_remote_fetcher.rb +51 -5
- data/test/rubygems/test_gem_request_set.rb +70 -0
- data/test/rubygems/test_gem_requirement.rb +53 -24
- data/test/rubygems/test_gem_security.rb +189 -43
- data/test/rubygems/test_gem_security_policy.rb +376 -0
- data/test/rubygems/test_gem_security_signer.rb +184 -0
- data/test/rubygems/test_gem_security_trust_dir.rb +94 -0
- data/test/rubygems/test_gem_server.rb +31 -36
- data/test/rubygems/test_gem_silent_ui.rb +2 -2
- data/test/rubygems/test_gem_source.rb +188 -0
- data/test/rubygems/test_gem_source_list.rb +87 -0
- data/test/rubygems/test_gem_source_local.rb +83 -0
- data/test/rubygems/test_gem_source_specific_file.rb +33 -0
- data/test/rubygems/test_gem_spec_fetcher.rb +91 -255
- data/test/rubygems/test_gem_specification.rb +293 -39
- data/test/rubygems/test_gem_uninstaller.rb +136 -13
- data/test/rubygems/test_gem_validator.rb +14 -41
- data/test/rubygems/test_gem_version.rb +15 -21
- data/test/rubygems/test_require.rb +193 -0
- data/test/rubygems/wrong_key_cert.pem +9 -0
- data/test/rubygems/wrong_key_cert_32.pem +9 -0
- metadata +171 -83
- metadata.gz.sig +1 -0
- data/CVE-2013-4287.txt +0 -36
- data/CVE-2013-4363.txt +0 -45
- data/ci_build.sh +0 -27
- data/cruise_config.rb +0 -32
- data/lib/rbconfig/datadir.rb +0 -13
- data/lib/rubygems/builder.rb +0 -99
- data/lib/rubygems/custom_require.rb +0 -69
- data/lib/rubygems/doc_manager.rb +0 -243
- data/lib/rubygems/format.rb +0 -82
- data/lib/rubygems/gem_openssl.rb +0 -90
- data/lib/rubygems/gem_path_searcher.rb +0 -172
- data/lib/rubygems/old_format.rb +0 -153
- data/lib/rubygems/package/f_sync_dir.rb +0 -23
- data/lib/rubygems/package/tar_input.rb +0 -234
- data/lib/rubygems/package/tar_output.rb +0 -146
- data/lib/rubygems/require_paths_builder.rb +0 -18
- data/lib/rubygems/source_index.rb +0 -406
- data/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem +0 -25
- data/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +0 -14
- data/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +0 -23
- data/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +0 -28
- data/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem +0 -20
- data/test/rubygems/test_bundled_ca.rb +0 -59
- data/test/rubygems/test_gem_builder.rb +0 -44
- data/test/rubygems/test_gem_doc_manager.rb +0 -32
- data/test/rubygems/test_gem_ext_builder.rb +0 -58
- data/test/rubygems/test_gem_format.rb +0 -88
- data/test/rubygems/test_gem_gem_path_searcher.rb +0 -94
- data/test/rubygems/test_gem_package_tar_input.rb +0 -129
- data/test/rubygems/test_gem_package_tar_output.rb +0 -101
- data/test/rubygems/test_gem_source_index.rb +0 -250
- data/util/update_bundled_ca_certificates.rb +0 -103
@@ -10,6 +10,10 @@ require 'rubygems/deprecate'
|
|
10
10
|
##
|
11
11
|
# Gem::DependencyList is used for installing and uninstalling gems in the
|
12
12
|
# correct order to avoid conflicts.
|
13
|
+
#--
|
14
|
+
# TODO: It appears that all but topo-sort functionality is being duplicated
|
15
|
+
# (or is planned to be duplicated) elsewhere in rubygems. Is the majority of
|
16
|
+
# this class necessary anymore? Especially #ok?, #why_not_ok?
|
13
17
|
|
14
18
|
class Gem::DependencyList
|
15
19
|
attr_reader :specs
|
@@ -27,19 +31,10 @@ class Gem::DependencyList
|
|
27
31
|
|
28
32
|
def self.from_specs
|
29
33
|
list = new
|
30
|
-
list.add(*Gem::Specification.
|
34
|
+
list.add(*Gem::Specification.to_a)
|
31
35
|
list
|
32
36
|
end
|
33
37
|
|
34
|
-
##
|
35
|
-
# Creates a DependencyList from a Gem::SourceIndex +source_index+
|
36
|
-
|
37
|
-
def self.from_source_index(ignored=nil)
|
38
|
-
warn "NOTE: DependencyList.from_source_index ignores it's arg" if ignored
|
39
|
-
|
40
|
-
from_specs
|
41
|
-
end
|
42
|
-
|
43
38
|
##
|
44
39
|
# Creates a new DependencyList. If +development+ is true, development
|
45
40
|
# dependencies will be included.
|
@@ -143,7 +138,7 @@ class Gem::DependencyList
|
|
143
138
|
# If removing the gemspec creates breaks a currently ok dependency, then it
|
144
139
|
# is NOT ok to remove the gemspec.
|
145
140
|
|
146
|
-
def ok_to_remove?(full_name)
|
141
|
+
def ok_to_remove?(full_name, check_dev=true)
|
147
142
|
gem_to_remove = find_name full_name
|
148
143
|
|
149
144
|
siblings = @specs.find_all { |s|
|
@@ -154,7 +149,9 @@ class Gem::DependencyList
|
|
154
149
|
deps = []
|
155
150
|
|
156
151
|
@specs.each do |spec|
|
157
|
-
spec.dependencies
|
152
|
+
check = check_dev ? spec.dependencies : spec.runtime_dependencies
|
153
|
+
|
154
|
+
check.each do |dep|
|
158
155
|
deps << dep if gem_to_remove.satisfies_requirement?(dep)
|
159
156
|
end
|
160
157
|
end
|
@@ -213,7 +210,7 @@ class Gem::DependencyList
|
|
213
210
|
@specs.each(&block)
|
214
211
|
end
|
215
212
|
|
216
|
-
def tsort_each_child(node
|
213
|
+
def tsort_each_child(node)
|
217
214
|
specs = @specs.sort.reverse
|
218
215
|
|
219
216
|
dependencies = node.runtime_dependencies
|
@@ -242,11 +239,6 @@ class Gem::DependencyList
|
|
242
239
|
def active_count(specs, ignored)
|
243
240
|
specs.count { |spec| ignored[spec.full_name].nil? }
|
244
241
|
end
|
245
|
-
end
|
246
242
|
|
247
|
-
class Gem::DependencyList
|
248
|
-
class << self
|
249
|
-
extend Gem::Deprecate
|
250
|
-
deprecate :from_source_index, "from_specs", 2011, 11
|
251
|
-
end
|
252
243
|
end
|
244
|
+
|
@@ -0,0 +1,562 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/dependency'
|
3
|
+
require 'rubygems/exceptions'
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'net/http'
|
7
|
+
|
8
|
+
module Gem
|
9
|
+
|
10
|
+
# Raised when a DependencyConflict reaches the toplevel.
|
11
|
+
# Indicates which dependencies were incompatible.
|
12
|
+
#
|
13
|
+
class DependencyResolutionError < Gem::Exception
|
14
|
+
def initialize(conflict)
|
15
|
+
@conflict = conflict
|
16
|
+
a, b = conflicting_dependencies
|
17
|
+
|
18
|
+
super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :conflict
|
22
|
+
|
23
|
+
def conflicting_dependencies
|
24
|
+
@conflict.conflicting_dependencies
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Raised when a dependency requests a gem for which there is
|
29
|
+
# no spec.
|
30
|
+
#
|
31
|
+
class UnsatisfiableDepedencyError < Gem::Exception
|
32
|
+
def initialize(dep)
|
33
|
+
super "unable to find any gem matching dependency '#{dep}'"
|
34
|
+
|
35
|
+
@dependency = dep
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :dependency
|
39
|
+
end
|
40
|
+
|
41
|
+
# Raised when dependencies conflict and create the inability to
|
42
|
+
# find a valid possible spec for a request.
|
43
|
+
#
|
44
|
+
class ImpossibleDependenciesError < Gem::Exception
|
45
|
+
def initialize(request, conflicts)
|
46
|
+
s = conflicts.size == 1 ? "" : "s"
|
47
|
+
super "detected #{conflicts.size} conflict#{s} with dependency '#{request.dependency}'"
|
48
|
+
@request = request
|
49
|
+
@conflicts = conflicts
|
50
|
+
end
|
51
|
+
|
52
|
+
def dependency
|
53
|
+
@request.dependency
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :conflicts
|
57
|
+
end
|
58
|
+
|
59
|
+
# Given a set of Gem::Dependency objects as +needed+ and a way
|
60
|
+
# to query the set of available specs via +set+, calculates
|
61
|
+
# a set of ActivationRequest objects which indicate all the specs
|
62
|
+
# that should be activated to meet the all the requirements.
|
63
|
+
#
|
64
|
+
class DependencyResolver
|
65
|
+
|
66
|
+
# Represents a specification retrieved via the rubygems.org
|
67
|
+
# API. This is used to avoid having to load the full
|
68
|
+
# Specification object when all we need is the name, version,
|
69
|
+
# and dependencies.
|
70
|
+
#
|
71
|
+
class APISpecification
|
72
|
+
def initialize(set, api_data)
|
73
|
+
@set = set
|
74
|
+
@name = api_data[:name]
|
75
|
+
@version = Gem::Version.new api_data[:number]
|
76
|
+
@dependencies = api_data[:dependencies].map do |name, ver|
|
77
|
+
Gem::Dependency.new name, ver.split(/\s*,\s*/)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :name, :version, :dependencies
|
82
|
+
|
83
|
+
def full_name
|
84
|
+
"#{@name}-#{@version}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# The global rubygems pool, available via the rubygems.org API.
|
89
|
+
# Returns instances of APISpecification.
|
90
|
+
#
|
91
|
+
class APISet
|
92
|
+
def initialize
|
93
|
+
@data = Hash.new { |h,k| h[k] = [] }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return data for all versions of the gem +name+.
|
97
|
+
#
|
98
|
+
def versions(name)
|
99
|
+
if @data.key?(name)
|
100
|
+
return @data[name]
|
101
|
+
end
|
102
|
+
|
103
|
+
u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{name}"
|
104
|
+
str = Net::HTTP.get(u)
|
105
|
+
|
106
|
+
Marshal.load(str).each do |ver|
|
107
|
+
@data[ver[:name]] << ver
|
108
|
+
end
|
109
|
+
|
110
|
+
@data[name]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return an array of APISpecification objects matching
|
114
|
+
# DependencyRequest +req+.
|
115
|
+
#
|
116
|
+
def find_all(req)
|
117
|
+
res = []
|
118
|
+
|
119
|
+
versions(req.name).each do |ver|
|
120
|
+
if req.dependency.match? req.name, ver[:number]
|
121
|
+
res << APISpecification.new(self, ver)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
res
|
126
|
+
end
|
127
|
+
|
128
|
+
# A hint run by the resolver to allow the Set to fetch
|
129
|
+
# data for DependencyRequests +reqs+.
|
130
|
+
#
|
131
|
+
def prefetch(reqs)
|
132
|
+
names = reqs.map { |r| r.dependency.name }
|
133
|
+
needed = names.find_all { |d| !@data.key?(d) }
|
134
|
+
|
135
|
+
return if needed.empty?
|
136
|
+
|
137
|
+
u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{needed.join ','}"
|
138
|
+
str = Net::HTTP.get(u)
|
139
|
+
|
140
|
+
Marshal.load(str).each do |ver|
|
141
|
+
@data[ver[:name]] << ver
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Represents a possible Specification object returned
|
147
|
+
# from IndexSet. Used to delay needed to download full
|
148
|
+
# Specification objects when only the +name+ and +version+
|
149
|
+
# are needed.
|
150
|
+
#
|
151
|
+
class IndexSpecification
|
152
|
+
def initialize(set, name, version, source, plat)
|
153
|
+
@set = set
|
154
|
+
@name = name
|
155
|
+
@version = version
|
156
|
+
@source = source
|
157
|
+
@platform = plat
|
158
|
+
|
159
|
+
@spec = nil
|
160
|
+
end
|
161
|
+
|
162
|
+
attr_reader :name, :version, :source
|
163
|
+
|
164
|
+
def full_name
|
165
|
+
"#{@name}-#{@version}"
|
166
|
+
end
|
167
|
+
|
168
|
+
def spec
|
169
|
+
@spec ||= @set.load_spec(@name, @version, @source)
|
170
|
+
end
|
171
|
+
|
172
|
+
def dependencies
|
173
|
+
spec.dependencies
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# The global rubygems pool represented via the traditional
|
178
|
+
# source index.
|
179
|
+
#
|
180
|
+
class IndexSet
|
181
|
+
def initialize
|
182
|
+
@f = Gem::SpecFetcher.fetcher
|
183
|
+
|
184
|
+
@all = Hash.new { |h,k| h[k] = [] }
|
185
|
+
|
186
|
+
list, _ = @f.available_specs(:released)
|
187
|
+
list.each do |uri, specs|
|
188
|
+
specs.each do |n|
|
189
|
+
@all[n.name] << [uri, n]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
@specs = {}
|
194
|
+
end
|
195
|
+
|
196
|
+
# Return an array of IndexSpecification objects matching
|
197
|
+
# DependencyRequest +req+.
|
198
|
+
#
|
199
|
+
def find_all(req)
|
200
|
+
res = []
|
201
|
+
|
202
|
+
name = req.dependency.name
|
203
|
+
|
204
|
+
@all[name].each do |uri, n|
|
205
|
+
if req.dependency.match? n
|
206
|
+
res << IndexSpecification.new(self, n.name, n.version,
|
207
|
+
uri, n.platform)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
res
|
212
|
+
end
|
213
|
+
|
214
|
+
# No prefetching needed since we load the whole index in
|
215
|
+
# initially.
|
216
|
+
#
|
217
|
+
def prefetch(gems)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Called from IndexSpecification to get a true Specification
|
221
|
+
# object.
|
222
|
+
#
|
223
|
+
def load_spec(name, ver, source)
|
224
|
+
key = "#{name}-#{ver}"
|
225
|
+
@specs[key] ||= source.fetch_spec(Gem::NameTuple.new(name, ver))
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# A set which represents the installed gems. Respects
|
230
|
+
# all the normal settings that control where to look
|
231
|
+
# for installed gems.
|
232
|
+
#
|
233
|
+
class CurrentSet
|
234
|
+
def find_all(req)
|
235
|
+
req.dependency.matching_specs
|
236
|
+
end
|
237
|
+
|
238
|
+
def prefetch(gems)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Create DependencyResolver object which will resolve
|
243
|
+
# the tree starting with +needed+ Depedency objects.
|
244
|
+
#
|
245
|
+
# +set+ is an object that provides where to look for
|
246
|
+
# specifications to satisify the Dependencies. This
|
247
|
+
# defaults to IndexSet, which will query rubygems.org.
|
248
|
+
#
|
249
|
+
def initialize(needed, set=IndexSet.new)
|
250
|
+
@set = set || IndexSet.new # Allow nil to mean IndexSet
|
251
|
+
@needed = needed
|
252
|
+
|
253
|
+
@conflicts = nil
|
254
|
+
end
|
255
|
+
|
256
|
+
# Provide a DependencyResolver that queries only against
|
257
|
+
# the already installed gems.
|
258
|
+
#
|
259
|
+
def self.for_current_gems(needed)
|
260
|
+
new needed, CurrentSet.new
|
261
|
+
end
|
262
|
+
|
263
|
+
# Contains all the conflicts encountered while doing resolution
|
264
|
+
#
|
265
|
+
attr_reader :conflicts
|
266
|
+
|
267
|
+
# Proceed with resolution! Returns an array of ActivationRequest
|
268
|
+
# objects.
|
269
|
+
#
|
270
|
+
def resolve
|
271
|
+
@conflicts = []
|
272
|
+
|
273
|
+
needed = @needed.map { |n| DependencyRequest.new(n, nil) }
|
274
|
+
|
275
|
+
res = resolve_for needed, []
|
276
|
+
|
277
|
+
if res.kind_of? DependencyConflict
|
278
|
+
raise DependencyResolutionError.new(res)
|
279
|
+
end
|
280
|
+
|
281
|
+
res
|
282
|
+
end
|
283
|
+
|
284
|
+
# Used internally to indicate that a dependency conflicted
|
285
|
+
# with a spec that would be activated.
|
286
|
+
#
|
287
|
+
class DependencyConflict
|
288
|
+
def initialize(dependency, activated, failed_dep=dependency)
|
289
|
+
@dependency = dependency
|
290
|
+
@activated = activated
|
291
|
+
@failed_dep = failed_dep
|
292
|
+
end
|
293
|
+
|
294
|
+
attr_reader :dependency, :activated
|
295
|
+
|
296
|
+
# Return the Specification that listed the dependency
|
297
|
+
#
|
298
|
+
def requester
|
299
|
+
@failed_dep.requester
|
300
|
+
end
|
301
|
+
|
302
|
+
def for_spec?(spec)
|
303
|
+
@dependency.name == spec.name
|
304
|
+
end
|
305
|
+
|
306
|
+
# Return the 2 dependency objects that conflicted
|
307
|
+
#
|
308
|
+
def conflicting_dependencies
|
309
|
+
[@failed_dep.dependency, @activated.request.dependency]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Used Internally. Wraps a Depedency object to also track
|
314
|
+
# which spec contained the Dependency.
|
315
|
+
#
|
316
|
+
class DependencyRequest
|
317
|
+
def initialize(dep, act)
|
318
|
+
@dependency = dep
|
319
|
+
@requester = act
|
320
|
+
end
|
321
|
+
|
322
|
+
attr_reader :dependency, :requester
|
323
|
+
|
324
|
+
def name
|
325
|
+
@dependency.name
|
326
|
+
end
|
327
|
+
|
328
|
+
def matches_spec?(spec)
|
329
|
+
@dependency.matches_spec? spec
|
330
|
+
end
|
331
|
+
|
332
|
+
def to_s
|
333
|
+
@dependency.to_s
|
334
|
+
end
|
335
|
+
|
336
|
+
def ==(other)
|
337
|
+
case other
|
338
|
+
when Dependency
|
339
|
+
@dependency == other
|
340
|
+
when DependencyRequest
|
341
|
+
@dependency == other.dep && @requester == other.requester
|
342
|
+
else
|
343
|
+
false
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
# Specifies a Specification object that should be activated.
|
349
|
+
# Also contains a dependency that was used to introduce this
|
350
|
+
# activation.
|
351
|
+
#
|
352
|
+
class ActivationRequest
|
353
|
+
def initialize(spec, req, others_possible=true)
|
354
|
+
@spec = spec
|
355
|
+
@request = req
|
356
|
+
@others_possible = others_possible
|
357
|
+
end
|
358
|
+
|
359
|
+
attr_reader :spec, :request
|
360
|
+
|
361
|
+
# Indicate if this activation is one of a set of possible
|
362
|
+
# requests for the same Dependency request.
|
363
|
+
#
|
364
|
+
def others_possible?
|
365
|
+
@others_possible
|
366
|
+
end
|
367
|
+
|
368
|
+
# Return the ActivationRequest that contained the dependency
|
369
|
+
# that we were activated for.
|
370
|
+
#
|
371
|
+
def parent
|
372
|
+
@request.requester
|
373
|
+
end
|
374
|
+
|
375
|
+
def name
|
376
|
+
@spec.name
|
377
|
+
end
|
378
|
+
|
379
|
+
def full_name
|
380
|
+
@spec.full_name
|
381
|
+
end
|
382
|
+
|
383
|
+
def version
|
384
|
+
@spec.version
|
385
|
+
end
|
386
|
+
|
387
|
+
def full_spec
|
388
|
+
Gem::Specification === @spec ? @spec : @spec.spec
|
389
|
+
end
|
390
|
+
|
391
|
+
def download(path)
|
392
|
+
if @spec.respond_to? :source
|
393
|
+
source = @spec.source
|
394
|
+
else
|
395
|
+
source = Gem.sources.first
|
396
|
+
end
|
397
|
+
|
398
|
+
source.download full_spec, path
|
399
|
+
end
|
400
|
+
|
401
|
+
def ==(other)
|
402
|
+
case other
|
403
|
+
when Gem::Specification
|
404
|
+
@spec == other
|
405
|
+
when ActivationRequest
|
406
|
+
@spec == other.spec && @request == other.request
|
407
|
+
else
|
408
|
+
false
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
##
|
413
|
+
# Indicates if the requested gem has already been installed.
|
414
|
+
|
415
|
+
def installed?
|
416
|
+
this_spec = full_spec
|
417
|
+
|
418
|
+
Gem::Specification.any? do |s|
|
419
|
+
s == this_spec
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def requests(s, act)
|
425
|
+
reqs = []
|
426
|
+
s.dependencies.each do |d|
|
427
|
+
next unless d.type == :runtime
|
428
|
+
reqs << DependencyRequest.new(d, act)
|
429
|
+
end
|
430
|
+
|
431
|
+
@set.prefetch(reqs)
|
432
|
+
|
433
|
+
reqs
|
434
|
+
end
|
435
|
+
|
436
|
+
# The meat of the algorithm. Given +needed+ DependencyRequest objects
|
437
|
+
# and +specs+ being a list to ActivationRequest, calculate a new list
|
438
|
+
# of ActivationRequest objects.
|
439
|
+
#
|
440
|
+
def resolve_for(needed, specs)
|
441
|
+
until needed.empty?
|
442
|
+
dep = needed.shift
|
443
|
+
|
444
|
+
# If there is already a spec activated for the requested name...
|
445
|
+
if existing = specs.find { |s| dep.name == s.name }
|
446
|
+
|
447
|
+
# then we're done since this new dep matches the
|
448
|
+
# existing spec.
|
449
|
+
next if dep.matches_spec? existing
|
450
|
+
|
451
|
+
# There is a conflict! We return the conflict
|
452
|
+
# object which will be seen by the caller and be
|
453
|
+
# handled at the right level.
|
454
|
+
|
455
|
+
# If the existing activation indicates that there
|
456
|
+
# are other possibles for it, then issue the conflict
|
457
|
+
# on the dep for the activation itself. Otherwise, issue
|
458
|
+
# it on the requester's request itself.
|
459
|
+
#
|
460
|
+
if existing.others_possible?
|
461
|
+
conflict = DependencyConflict.new(dep, existing)
|
462
|
+
else
|
463
|
+
depreq = existing.request.requester.request
|
464
|
+
conflict = DependencyConflict.new(depreq, existing, dep)
|
465
|
+
end
|
466
|
+
@conflicts << conflict
|
467
|
+
|
468
|
+
return conflict
|
469
|
+
end
|
470
|
+
|
471
|
+
# Get a list of all specs that satisfy dep
|
472
|
+
possible = @set.find_all(dep)
|
473
|
+
|
474
|
+
case possible.size
|
475
|
+
when 0
|
476
|
+
# If there are none, then our work here is done.
|
477
|
+
raise UnsatisfiableDepedencyError.new(dep)
|
478
|
+
when 1
|
479
|
+
# If there is one, then we just add it to specs
|
480
|
+
# and process the specs dependencies by adding
|
481
|
+
# them to needed.
|
482
|
+
|
483
|
+
spec = possible.first
|
484
|
+
act = ActivationRequest.new(spec, dep, false)
|
485
|
+
|
486
|
+
specs << act
|
487
|
+
|
488
|
+
# Put the deps for at the beginning of needed
|
489
|
+
# rather than the end to match the depth first
|
490
|
+
# searching done by the multiple case code below.
|
491
|
+
#
|
492
|
+
# This keeps the error messages consistent.
|
493
|
+
needed = requests(spec, act) + needed
|
494
|
+
else
|
495
|
+
# There are multiple specs for this dep. This is
|
496
|
+
# the case that this class is built to handle.
|
497
|
+
|
498
|
+
# Sort them so that we try the highest versions
|
499
|
+
# first.
|
500
|
+
possible = possible.sort_by { |s| s.version }
|
501
|
+
|
502
|
+
# We track the conflicts seen so that we can report them
|
503
|
+
# to help the user figure out how to fix the situation.
|
504
|
+
conflicts = []
|
505
|
+
|
506
|
+
# To figure out which to pick, we keep resolving
|
507
|
+
# given each one being activated and if there isn't
|
508
|
+
# a conflict, we know we've found a full set.
|
509
|
+
#
|
510
|
+
# We use an until loop rather than #reverse_each
|
511
|
+
# to keep the stack short since we're using a recursive
|
512
|
+
# algorithm.
|
513
|
+
#
|
514
|
+
until possible.empty?
|
515
|
+
s = possible.pop
|
516
|
+
|
517
|
+
# Recursively call #resolve_for with this spec
|
518
|
+
# and add it's dependencies into the picture...
|
519
|
+
|
520
|
+
act = ActivationRequest.new(s, dep)
|
521
|
+
|
522
|
+
try = requests(s, act) + needed
|
523
|
+
|
524
|
+
res = resolve_for(try, specs + [act])
|
525
|
+
|
526
|
+
# While trying to resolve these dependencies, there may
|
527
|
+
# be a conflict!
|
528
|
+
|
529
|
+
if res.kind_of? DependencyConflict
|
530
|
+
# The conflict might be created not by this invocation
|
531
|
+
# but rather one up the stack, so if we can't attempt
|
532
|
+
# to resolve this conflict (conflict isn't with the spec +s+)
|
533
|
+
# then just return it so the caller can try to sort it out.
|
534
|
+
return res unless res.for_spec? s
|
535
|
+
|
536
|
+
# Otherwise, this is a conflict that we can attempt to fix
|
537
|
+
conflicts << [s, res]
|
538
|
+
|
539
|
+
# Optimization:
|
540
|
+
#
|
541
|
+
# Because the conflict indicates the dependency that trigger
|
542
|
+
# it, we can prune possible based on this new information.
|
543
|
+
#
|
544
|
+
# This cuts down on the number of iterations needed.
|
545
|
+
possible.delete_if { |x| !res.dependency.matches_spec? x }
|
546
|
+
else
|
547
|
+
# No conflict, return the specs
|
548
|
+
return res
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
# We tried all possibles and nothing worked, so we let the user
|
553
|
+
# know and include as much information about the problem since
|
554
|
+
# the user is going to have to take action to fix this.
|
555
|
+
raise ImpossibleDependenciesError.new(dep, conflicts)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
specs
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|