1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/dependency_resolver'
require 'rubygems/dependency_list'
require 'rubygems/installer'
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_reader :always_install
attr_reader :dependencies
attr_accessor :development
##
# 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 = []
@development = false
@requests = []
@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
@dependencies << Gem::Dependency.new(name, reqs)
end
##
# Add +deps+ Gem::Dependency objects to the set.
def import deps
@dependencies.concat deps
end
def install options, &block
if dir = options[:install_dir]
return install_into dir, false, options, &block
end
cache_dir = options[:cache_dir] || Gem.dir
specs = []
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?
specs << inst.install
end
specs
end
def install_into dir, force = true, options = {}
existing = force ? [] : specs_in(dir)
existing.delete_if { |s| @always_install.include? s }
dir = File.expand_path dir
installed = []
sorted_requests.each do |req|
if existing.find { |s| s.full_name == req.spec.full_name }
yield req, nil if block_given?
next
end
path = req.download(dir)
unless path then # already installed
yield req, nil if block_given?
next
end
options[:install_dir] = dir
options[:only_install_dir] = true
inst = Gem::Installer.new path, options
yield req, inst if block_given?
inst.install
installed << req
end
installed
end
##
# Load a dependency management file.
def load_gemdeps path
@vendor_set = Gem::DependencyResolver::VendorSet.new
gf = Gem::RequestSet::GemDependencyAPI.new self, path
gf.load
end
##
# Resolve the requested dependencies and return an Array of Specification
# objects to be activated.
def resolve set = Gem::DependencyResolver::IndexSet.new
sets = [set, @vendor_set].compact
set = if sets.size == 1 then
sets.first
else
Gem::DependencyResolver.compose_sets(*sets)
end
resolver = Gem::DependencyResolver.new @dependencies, set
resolver.development = @development
resolver.soft_missing = @soft_missing
@requests = resolver.resolve
end
##
# Resolve the requested dependencies against the gems available via Gem.path
# and return an Array of Specification objects to be activated.
def resolve_current
resolve Gem::DependencyResolver::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 }
if match
begin
yield match
rescue TSort::Cyclic
end
else
unless @soft_missing
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
end
end
end
end
end
require 'rubygems/request_set/gem_dependency_api'
|