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.

Files changed (241) hide show
  1. checksums.yaml +6 -6
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +3 -0
  4. data/.autotest +6 -3
  5. data/History.txt +137 -63
  6. data/LICENSE.txt +1 -5
  7. data/Manifest.txt +69 -32
  8. data/README.rdoc +11 -9
  9. data/Rakefile +24 -38
  10. data/bin/gem +0 -9
  11. data/bin/update_rubygems +1 -0
  12. data/lib/rubygems.rb +193 -405
  13. data/lib/rubygems/available_set.rb +95 -0
  14. data/lib/rubygems/command.rb +88 -45
  15. data/lib/rubygems/command_manager.rb +67 -40
  16. data/lib/rubygems/commands/build_command.rb +5 -23
  17. data/lib/rubygems/commands/cert_command.rb +199 -57
  18. data/lib/rubygems/commands/check_command.rb +14 -39
  19. data/lib/rubygems/commands/cleanup_command.rb +9 -1
  20. data/lib/rubygems/commands/contents_command.rb +30 -12
  21. data/lib/rubygems/commands/dependency_command.rb +3 -8
  22. data/lib/rubygems/commands/environment_command.rb +13 -8
  23. data/lib/rubygems/commands/fetch_command.rb +3 -16
  24. data/lib/rubygems/commands/generate_index_command.rb +7 -47
  25. data/lib/rubygems/commands/help_command.rb +1 -1
  26. data/lib/rubygems/commands/install_command.rb +69 -36
  27. data/lib/rubygems/commands/list_command.rb +6 -4
  28. data/lib/rubygems/commands/lock_command.rb +1 -1
  29. data/lib/rubygems/commands/mirror_command.rb +17 -0
  30. data/lib/rubygems/commands/outdated_command.rb +6 -3
  31. data/lib/rubygems/commands/owner_command.rb +13 -5
  32. data/lib/rubygems/commands/pristine_command.rb +19 -4
  33. data/lib/rubygems/commands/push_command.rb +12 -1
  34. data/lib/rubygems/commands/query_command.rb +43 -27
  35. data/lib/rubygems/commands/rdoc_command.rb +23 -28
  36. data/lib/rubygems/commands/search_command.rb +4 -18
  37. data/lib/rubygems/commands/server_command.rb +1 -1
  38. data/lib/rubygems/commands/setup_command.rb +124 -38
  39. data/lib/rubygems/commands/sources_command.rb +16 -16
  40. data/lib/rubygems/commands/specification_command.rb +11 -13
  41. data/lib/rubygems/commands/uninstall_command.rb +24 -7
  42. data/lib/rubygems/commands/unpack_command.rb +7 -3
  43. data/lib/rubygems/commands/update_command.rb +22 -36
  44. data/lib/rubygems/commands/yank_command.rb +98 -0
  45. data/lib/rubygems/compatibility.rb +51 -0
  46. data/lib/rubygems/config_file.rb +82 -54
  47. data/lib/rubygems/core_ext/kernel_gem.rb +53 -0
  48. data/lib/rubygems/core_ext/kernel_require.rb +119 -0
  49. data/lib/rubygems/defaults.rb +10 -21
  50. data/lib/rubygems/dependency.rb +61 -10
  51. data/lib/rubygems/dependency_installer.rb +157 -69
  52. data/lib/rubygems/dependency_list.rb +11 -19
  53. data/lib/rubygems/dependency_resolver.rb +562 -0
  54. data/lib/rubygems/deprecate.rb +40 -40
  55. data/lib/rubygems/errors.rb +77 -24
  56. data/lib/rubygems/exceptions.rb +25 -7
  57. data/lib/rubygems/ext/builder.rb +20 -23
  58. data/lib/rubygems/ext/configure_builder.rb +2 -2
  59. data/lib/rubygems/ext/ext_conf_builder.rb +5 -45
  60. data/lib/rubygems/ext/rake_builder.rb +2 -2
  61. data/lib/rubygems/gem_runner.rb +3 -16
  62. data/lib/rubygems/gemcutter_utilities.rb +22 -7
  63. data/lib/rubygems/indexer.rb +6 -159
  64. data/lib/rubygems/install_message.rb +12 -0
  65. data/lib/rubygems/install_update_options.rb +56 -18
  66. data/lib/rubygems/installer.rb +244 -134
  67. data/lib/rubygems/installer_test_case.rb +71 -19
  68. data/lib/rubygems/mock_gem_ui.rb +17 -0
  69. data/lib/rubygems/name_tuple.rb +110 -0
  70. data/lib/rubygems/package.rb +514 -43
  71. data/lib/rubygems/package/digest_io.rb +64 -0
  72. data/lib/rubygems/package/old.rb +147 -0
  73. data/lib/rubygems/package/tar_header.rb +18 -55
  74. data/lib/rubygems/package/tar_reader.rb +20 -3
  75. data/lib/rubygems/package/tar_writer.rb +63 -7
  76. data/lib/rubygems/package_task.rb +3 -4
  77. data/lib/rubygems/path_support.rb +14 -7
  78. data/lib/rubygems/platform.rb +19 -26
  79. data/lib/rubygems/rdoc.rb +316 -0
  80. data/lib/rubygems/remote_fetcher.rb +117 -54
  81. data/lib/rubygems/request_set.rb +182 -0
  82. data/lib/rubygems/requirement.rb +63 -26
  83. data/lib/rubygems/security.rb +295 -555
  84. data/lib/rubygems/security/policies.rb +115 -0
  85. data/lib/rubygems/security/policy.rb +227 -0
  86. data/lib/rubygems/security/signer.rb +136 -0
  87. data/lib/rubygems/security/trust_dir.rb +104 -0
  88. data/lib/rubygems/server.rb +45 -55
  89. data/lib/rubygems/source.rb +144 -0
  90. data/lib/rubygems/source_list.rb +87 -0
  91. data/lib/rubygems/source_local.rb +92 -0
  92. data/lib/rubygems/source_specific_file.rb +28 -0
  93. data/lib/rubygems/spec_fetcher.rb +116 -184
  94. data/lib/rubygems/specification.rb +731 -335
  95. data/lib/rubygems/ssl_certs/AddTrustExternalCARoot.pem +88 -30
  96. data/lib/rubygems/ssl_certs/Entrust_net-Secure-Server-Certification-Authority.pem +90 -0
  97. data/lib/rubygems/ssl_certs/VerisignClass3PublicPrimaryCertificationAuthority-G2.pem +57 -0
  98. data/lib/rubygems/syck_hack.rb +2 -0
  99. data/lib/rubygems/test_case.rb +199 -109
  100. data/lib/rubygems/test_utilities.rb +25 -5
  101. data/lib/rubygems/uninstaller.rb +62 -20
  102. data/lib/rubygems/user_interaction.rb +10 -0
  103. data/lib/rubygems/validator.rb +33 -40
  104. data/lib/rubygems/version.rb +19 -8
  105. data/setup.rb +8 -1
  106. data/test/rubygems/alternate_cert.pem +9 -0
  107. data/test/rubygems/alternate_cert_32.pem +9 -0
  108. data/test/rubygems/alternate_key.pem +9 -0
  109. data/test/rubygems/bad_rake.rb +1 -0
  110. data/test/rubygems/child_cert.pem +9 -0
  111. data/test/rubygems/child_cert_32.pem +9 -0
  112. data/test/rubygems/child_key.pem +9 -0
  113. data/test/rubygems/data/null-type.gemspec.rz +0 -0
  114. data/test/rubygems/expired_cert.pem +9 -0
  115. data/test/rubygems/future_cert.pem +9 -0
  116. data/test/rubygems/future_cert_32.pem +9 -0
  117. data/test/rubygems/good_rake.rb +1 -0
  118. data/test/rubygems/grandchild_cert.pem +9 -0
  119. data/test/rubygems/grandchild_cert_32.pem +9 -0
  120. data/test/rubygems/grandchild_key.pem +9 -0
  121. data/test/rubygems/invalid_issuer_cert.pem +9 -0
  122. data/test/rubygems/invalid_issuer_cert_32.pem +9 -0
  123. data/test/rubygems/invalid_key.pem +9 -0
  124. data/test/rubygems/invalid_signer_cert.pem +9 -0
  125. data/test/rubygems/invalid_signer_cert_32.pem +9 -0
  126. data/test/rubygems/invalidchild_cert.pem +9 -0
  127. data/test/rubygems/invalidchild_cert_32.pem +9 -0
  128. data/test/rubygems/invalidchild_key.pem +9 -0
  129. data/test/rubygems/plugin/exception/rubygems_plugin.rb +1 -1
  130. data/test/rubygems/plugin/standarderror/rubygems_plugin.rb +1 -1
  131. data/test/rubygems/private_key.pem +7 -25
  132. data/test/rubygems/public_cert.pem +8 -18
  133. data/test/rubygems/public_cert_32.pem +10 -0
  134. data/test/rubygems/public_key.pem +4 -0
  135. data/test/rubygems/rubygems/commands/crash_command.rb +1 -1
  136. data/test/rubygems/test_config.rb +4 -6
  137. data/test/rubygems/test_deprecate.rb +76 -0
  138. data/test/rubygems/test_gem.rb +318 -83
  139. data/test/rubygems/test_gem_available_set.rb +106 -0
  140. data/test/rubygems/test_gem_command.rb +10 -0
  141. data/test/rubygems/test_gem_command_manager.rb +55 -9
  142. data/test/rubygems/test_gem_commands_build_command.rb +11 -19
  143. data/test/rubygems/test_gem_commands_cert_command.rb +441 -42
  144. data/test/rubygems/test_gem_commands_cleanup_command.rb +29 -1
  145. data/test/rubygems/test_gem_commands_contents_command.rb +23 -0
  146. data/test/rubygems/test_gem_commands_dependency_command.rb +5 -0
  147. data/test/rubygems/test_gem_commands_fetch_command.rb +19 -20
  148. data/test/rubygems/test_gem_commands_generate_index_command.rb +2 -83
  149. data/test/rubygems/test_gem_commands_help_command.rb +2 -1
  150. data/test/rubygems/test_gem_commands_install_command.rb +647 -48
  151. data/test/rubygems/test_gem_commands_mirror.rb +32 -0
  152. data/test/rubygems/test_gem_commands_owner_command.rb +4 -8
  153. data/test/rubygems/test_gem_commands_pristine_command.rb +99 -4
  154. data/test/rubygems/test_gem_commands_push_command.rb +62 -8
  155. data/test/rubygems/test_gem_commands_query_command.rb +51 -0
  156. data/test/rubygems/test_gem_commands_search_command.rb +25 -0
  157. data/test/rubygems/test_gem_commands_setup_command.rb +45 -0
  158. data/test/rubygems/test_gem_commands_sources_command.rb +21 -6
  159. data/test/rubygems/test_gem_commands_specification_command.rb +33 -1
  160. data/test/rubygems/test_gem_commands_uninstall_command.rb +91 -31
  161. data/test/rubygems/test_gem_commands_unpack_command.rb +3 -3
  162. data/test/rubygems/test_gem_commands_update_command.rb +56 -38
  163. data/test/rubygems/test_gem_commands_which_command.rb +4 -4
  164. data/test/rubygems/test_gem_commands_yank_command.rb +97 -0
  165. data/test/rubygems/test_gem_config_file.rb +66 -21
  166. data/test/rubygems/test_gem_dependency.rb +46 -0
  167. data/test/rubygems/test_gem_dependency_installer.rb +228 -18
  168. data/test/rubygems/test_gem_dependency_list.rb +0 -9
  169. data/test/rubygems/test_gem_dependency_resolver.rb +327 -0
  170. data/test/rubygems/test_gem_ext_configure_builder.rb +4 -4
  171. data/test/rubygems/test_gem_ext_ext_conf_builder.rb +21 -49
  172. data/test/rubygems/test_gem_ext_rake_builder.rb +13 -13
  173. data/test/rubygems/test_gem_gem_runner.rb +27 -5
  174. data/test/rubygems/test_gem_gemcutter_utilities.rb +19 -0
  175. data/test/rubygems/test_gem_indexer.rb +14 -227
  176. data/test/rubygems/test_gem_install_update_options.rb +83 -3
  177. data/test/rubygems/test_gem_installer.rb +211 -236
  178. data/test/rubygems/test_gem_local_remote_options.rb +8 -2
  179. data/test/rubygems/test_gem_name_tuple.rb +15 -0
  180. data/test/rubygems/test_gem_package.rb +547 -0
  181. data/test/rubygems/test_gem_package_old.rb +37 -0
  182. data/test/rubygems/test_gem_package_tar_reader.rb +32 -0
  183. data/test/rubygems/test_gem_package_tar_writer.rb +84 -1
  184. data/test/rubygems/test_gem_path_support.rb +4 -30
  185. data/test/rubygems/test_gem_platform.rb +3 -6
  186. data/test/rubygems/test_gem_rdoc.rb +245 -0
  187. data/test/rubygems/test_gem_remote_fetcher.rb +51 -5
  188. data/test/rubygems/test_gem_request_set.rb +70 -0
  189. data/test/rubygems/test_gem_requirement.rb +53 -24
  190. data/test/rubygems/test_gem_security.rb +189 -43
  191. data/test/rubygems/test_gem_security_policy.rb +376 -0
  192. data/test/rubygems/test_gem_security_signer.rb +184 -0
  193. data/test/rubygems/test_gem_security_trust_dir.rb +94 -0
  194. data/test/rubygems/test_gem_server.rb +31 -36
  195. data/test/rubygems/test_gem_silent_ui.rb +2 -2
  196. data/test/rubygems/test_gem_source.rb +188 -0
  197. data/test/rubygems/test_gem_source_list.rb +87 -0
  198. data/test/rubygems/test_gem_source_local.rb +83 -0
  199. data/test/rubygems/test_gem_source_specific_file.rb +33 -0
  200. data/test/rubygems/test_gem_spec_fetcher.rb +91 -255
  201. data/test/rubygems/test_gem_specification.rb +293 -39
  202. data/test/rubygems/test_gem_uninstaller.rb +136 -13
  203. data/test/rubygems/test_gem_validator.rb +14 -41
  204. data/test/rubygems/test_gem_version.rb +15 -21
  205. data/test/rubygems/test_require.rb +193 -0
  206. data/test/rubygems/wrong_key_cert.pem +9 -0
  207. data/test/rubygems/wrong_key_cert_32.pem +9 -0
  208. metadata +171 -83
  209. metadata.gz.sig +1 -0
  210. data/CVE-2013-4287.txt +0 -36
  211. data/CVE-2013-4363.txt +0 -45
  212. data/ci_build.sh +0 -27
  213. data/cruise_config.rb +0 -32
  214. data/lib/rbconfig/datadir.rb +0 -13
  215. data/lib/rubygems/builder.rb +0 -99
  216. data/lib/rubygems/custom_require.rb +0 -69
  217. data/lib/rubygems/doc_manager.rb +0 -243
  218. data/lib/rubygems/format.rb +0 -82
  219. data/lib/rubygems/gem_openssl.rb +0 -90
  220. data/lib/rubygems/gem_path_searcher.rb +0 -172
  221. data/lib/rubygems/old_format.rb +0 -153
  222. data/lib/rubygems/package/f_sync_dir.rb +0 -23
  223. data/lib/rubygems/package/tar_input.rb +0 -234
  224. data/lib/rubygems/package/tar_output.rb +0 -146
  225. data/lib/rubygems/require_paths_builder.rb +0 -18
  226. data/lib/rubygems/source_index.rb +0 -406
  227. data/lib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem +0 -25
  228. data/lib/rubygems/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +0 -14
  229. data/lib/rubygems/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +0 -23
  230. data/lib/rubygems/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +0 -28
  231. data/lib/rubygems/ssl_certs/GeoTrustGlobalCA.pem +0 -20
  232. data/test/rubygems/test_bundled_ca.rb +0 -59
  233. data/test/rubygems/test_gem_builder.rb +0 -44
  234. data/test/rubygems/test_gem_doc_manager.rb +0 -32
  235. data/test/rubygems/test_gem_ext_builder.rb +0 -58
  236. data/test/rubygems/test_gem_format.rb +0 -88
  237. data/test/rubygems/test_gem_gem_path_searcher.rb +0 -94
  238. data/test/rubygems/test_gem_package_tar_input.rb +0 -129
  239. data/test/rubygems/test_gem_package_tar_output.rb +0 -101
  240. data/test/rubygems/test_gem_source_index.rb +0 -250
  241. 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.map)
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.each do |dep|
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, &block)
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