sprout 0.7.236-mswin32 → 0.7.237-mswin32
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprout might be problematic. Click here for more details.
- data/lib/sprout/bundle_resolver.rb +349 -0
- data/lib/sprout/tasks/gem_wrap_task.rb +12 -0
- data/lib/sprout/version.rb +1 -1
- data/lib/sprout.rb +1 -0
- metadata +3 -2
@@ -0,0 +1,349 @@
|
|
1
|
+
# Taken from http://github.com/carlhuda/bundler
|
2
|
+
module Gem
|
3
|
+
class Dependency
|
4
|
+
def required_by
|
5
|
+
@required_by ||= []
|
6
|
+
end
|
7
|
+
end
|
8
|
+
class Specification
|
9
|
+
def required_by
|
10
|
+
@required_by ||= []
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Sprout
|
16
|
+
|
17
|
+
class Index
|
18
|
+
def self.from_installed_gems
|
19
|
+
#Source::SystemGems.new.specs
|
20
|
+
index = Index.new
|
21
|
+
Gem::cache.each do |name, spec|
|
22
|
+
index << spec
|
23
|
+
end
|
24
|
+
index
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.from_cached_specs(path)
|
28
|
+
# index = Index.new
|
29
|
+
# Sprout::cache
|
30
|
+
# Dir["#{path}/*.gem"].each do |gemfile|
|
31
|
+
# spec = Gem::Format.from_file_by_path(gemfile).spec
|
32
|
+
# index << spec
|
33
|
+
# end
|
34
|
+
|
35
|
+
# index
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@cache = {}
|
40
|
+
@specs = Hash.new { |h,k| h[k] = [] }
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize_copy(o)
|
44
|
+
super
|
45
|
+
@cache = {}
|
46
|
+
@specs = Hash.new { |h,k| h[k] = [] }
|
47
|
+
merge!(o)
|
48
|
+
end
|
49
|
+
|
50
|
+
def empty?
|
51
|
+
each { return false }
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def search(query)
|
56
|
+
case query
|
57
|
+
when Gem::Specification then search_by_spec(query)
|
58
|
+
when String then @specs[query]
|
59
|
+
else search_by_dependency(query)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
alias [] search
|
64
|
+
|
65
|
+
def <<(spec)
|
66
|
+
arr = @specs[spec.name]
|
67
|
+
|
68
|
+
arr.delete_if do |s|
|
69
|
+
s.version == spec.version && s.platform == spec.platform
|
70
|
+
end
|
71
|
+
|
72
|
+
arr << spec
|
73
|
+
spec
|
74
|
+
end
|
75
|
+
|
76
|
+
def each(&blk)
|
77
|
+
@specs.values.each do |specs|
|
78
|
+
specs.each(&blk)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def merge!(other)
|
83
|
+
other.each do |spec|
|
84
|
+
self << spec
|
85
|
+
end
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def merge(other)
|
90
|
+
dup.merge!(other)
|
91
|
+
end
|
92
|
+
|
93
|
+
def freeze
|
94
|
+
@specs.each do |k,v|
|
95
|
+
v.freeze
|
96
|
+
end
|
97
|
+
super
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def search_by_spec(spec)
|
103
|
+
@specs[spec.name].select { |s| s.version == spec.version }
|
104
|
+
end
|
105
|
+
|
106
|
+
def search_by_dependency(dependency)
|
107
|
+
@cache[dependency.hash] ||= begin
|
108
|
+
specs = @specs[dependency.name]
|
109
|
+
|
110
|
+
#wants_prerelease = dependency.requirement.prerelease?
|
111
|
+
#only_prerelease = specs.all? {|spec| spec.version.prerelease? }
|
112
|
+
found = specs.select { |spec| dependency =~ spec }
|
113
|
+
|
114
|
+
#unless wants_prerelease || only_prerelease
|
115
|
+
# found.reject! { |spec| spec.version.prerelease? }
|
116
|
+
#end
|
117
|
+
|
118
|
+
found.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\\0" : s.platform.to_s] }
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class Resolver
|
126
|
+
attr_reader :errors
|
127
|
+
|
128
|
+
# Figures out the best possible configuration of gems that satisfies
|
129
|
+
# the list of passed dependencies and any child dependencies without
|
130
|
+
# causing any gem activation errors.
|
131
|
+
#
|
132
|
+
# ==== Parameters
|
133
|
+
# *dependencies<Gem::Dependency>:: The list of dependencies to resolve
|
134
|
+
#
|
135
|
+
# ==== Returns
|
136
|
+
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
|
137
|
+
# collection of gemspecs is returned. Otherwise, nil is returned.
|
138
|
+
def self.resolve(requirements, index, source_requirements = {})
|
139
|
+
resolver = new(index, source_requirements)
|
140
|
+
result = catch(:success) do
|
141
|
+
resolver.resolve(requirements, {})
|
142
|
+
output = resolver.errors.inject("") do |o, (conflict, (origin, requirement))|
|
143
|
+
if origin
|
144
|
+
o << " Conflict on: #{conflict.inspect}:\\n"
|
145
|
+
o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\\n"
|
146
|
+
o << " * #{requirement} required by #{requirement.required_by.first}\\n"
|
147
|
+
else
|
148
|
+
o << " #{requirement} not found in any of the sources\\n"
|
149
|
+
o << " required by #{requirement.required_by.first}\\n"
|
150
|
+
end
|
151
|
+
o << " All possible versions of origin requirements conflict."
|
152
|
+
end
|
153
|
+
raise VersionConflict, "No compatible versions could be found for required dependencies:\\n #{output}"
|
154
|
+
nil
|
155
|
+
end
|
156
|
+
if result
|
157
|
+
# Order gems in order of dependencies. Every gem's dependency is at
|
158
|
+
# a smaller index in the array.
|
159
|
+
ordered = []
|
160
|
+
result.values.each do |spec1|
|
161
|
+
index = nil
|
162
|
+
place = ordered.detect do |spec2|
|
163
|
+
spec1.dependencies.any? { |d| d.name == spec2.name }
|
164
|
+
end
|
165
|
+
place ?
|
166
|
+
ordered.insert(ordered.index(place), spec1) :
|
167
|
+
ordered << spec1
|
168
|
+
end
|
169
|
+
ordered.reverse
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.resolve_libraries(library_names)
|
174
|
+
index = Index.from_installed_gems
|
175
|
+
lib_specs = library_names.map {|lib| Sprout.find_gem_spec "sprout-#{lib}-library" }
|
176
|
+
resolved_specs = resolve lib_specs, index
|
177
|
+
library_names_with_dependencies = []
|
178
|
+
resolved_specs.each do |l|
|
179
|
+
if l.name =~ /\Asprout-(.*)-library\Z/
|
180
|
+
library_names_with_dependencies << $1
|
181
|
+
end
|
182
|
+
end
|
183
|
+
library_names_with_dependencies
|
184
|
+
end
|
185
|
+
|
186
|
+
def initialize(index, source_requirements)
|
187
|
+
@errors = {}
|
188
|
+
@stack = []
|
189
|
+
@index = index
|
190
|
+
@source_requirements = source_requirements
|
191
|
+
end
|
192
|
+
|
193
|
+
def debug
|
194
|
+
puts yield if defined?($debug) && $debug
|
195
|
+
end
|
196
|
+
|
197
|
+
def resolve(reqs, activated)
|
198
|
+
# If the requirements are empty, then we are in a success state. Aka, all
|
199
|
+
# gem dependencies have been resolved.
|
200
|
+
throw :success, activated if reqs.empty?
|
201
|
+
|
202
|
+
debug { STDIN.gets ; print "\\e[2J\\e[f" ; "==== Iterating ====\\n\\n" }
|
203
|
+
|
204
|
+
# Sort dependencies so that the ones that are easiest to resolve are first.
|
205
|
+
# Easiest to resolve is defined by:
|
206
|
+
# 1) Is this gem already activated?
|
207
|
+
# 2) Do the version requirements include prereleased gems?
|
208
|
+
# 3) Sort by number of gems available in the source.
|
209
|
+
reqs = reqs.sort_by do |a|
|
210
|
+
[ activated[a.name] ? 0 : 1,
|
211
|
+
#a.requirement.prerelease? ? 0 : 1,
|
212
|
+
@errors[a.name] ? 0 : 1,
|
213
|
+
activated[a.name] ? 0 : search(a).size ]
|
214
|
+
end
|
215
|
+
|
216
|
+
debug { "Activated:\\n" + activated.values.map { |a| " #{a.name} (#{a.version})" }.join("\\n") }
|
217
|
+
debug { "Requirements:\\n" + reqs.map { |r| " #{r.name} (#{r.requirement})"}.join("\\n") }
|
218
|
+
|
219
|
+
activated = activated.dup
|
220
|
+
# Pull off the first requirement so that we can resolve it
|
221
|
+
current = reqs.shift
|
222
|
+
|
223
|
+
debug { "Attempting:\\n #{current.name} (#{current.requirement})"}
|
224
|
+
|
225
|
+
# Check if the gem has already been activated, if it has, we will make sure
|
226
|
+
# that the currently activated gem satisfies the requirement.
|
227
|
+
if existing = activated[current.name]
|
228
|
+
if current.requirement.satisfied_by?(existing.version)
|
229
|
+
debug { " * [SUCCESS] Already activated" }
|
230
|
+
@errors.delete(existing.name)
|
231
|
+
# Since the current requirement is satisfied, we can continue resolving
|
232
|
+
# the remaining requirements.
|
233
|
+
resolve(reqs, activated)
|
234
|
+
else
|
235
|
+
debug { " * [FAIL] Already activated" }
|
236
|
+
@errors[existing.name] = [existing, current]
|
237
|
+
debug { current.required_by.map {|d| " * #{d.name} (#{d.requirement})" }.join("\\n") }
|
238
|
+
# debug { " * All current conflicts:\\n" + @errors.keys.map { |c| " - #{c}" }.join("\\n") }
|
239
|
+
# Since the current requirement conflicts with an activated gem, we need
|
240
|
+
# to backtrack to the current requirement's parent and try another version
|
241
|
+
# of it (maybe the current requirement won't be present anymore). If the
|
242
|
+
# current requirement is a root level requirement, we need to jump back to
|
243
|
+
# where the conflicting gem was activated.
|
244
|
+
parent = current.required_by.last || existing.required_by.last
|
245
|
+
# We track the spot where the current gem was activated because we need
|
246
|
+
# to keep a list of every spot a failure happened.
|
247
|
+
debug { " -> Jumping to: #{parent.name}" }
|
248
|
+
throw parent.name, existing.required_by.last.name
|
249
|
+
end
|
250
|
+
else
|
251
|
+
# There are no activated gems for the current requirement, so we are going
|
252
|
+
# to find all gems that match the current requirement and try them in decending
|
253
|
+
# order. We also need to keep a set of all conflicts that happen while trying
|
254
|
+
# this gem. This is so that if no versions work, we can figure out the best
|
255
|
+
# place to backtrack to.
|
256
|
+
conflicts = Set.new
|
257
|
+
|
258
|
+
# Fetch all gem versions matching the requirement
|
259
|
+
#
|
260
|
+
# TODO: Warn / error when no matching versions are found.
|
261
|
+
matching_versions = search(current)
|
262
|
+
|
263
|
+
if matching_versions.empty?
|
264
|
+
if current.required_by.empty?
|
265
|
+
if current.source
|
266
|
+
name = current.name
|
267
|
+
versions = @source_requirements[name][name].map { |s| s.version }
|
268
|
+
message = "Could not find gem '#{current}' in #{current.source}.\\n"
|
269
|
+
if versions.any?
|
270
|
+
message << "Source contains '#{current.name}' at: #{versions.join(', ')}"
|
271
|
+
else
|
272
|
+
message << "Source does not contain any versions of '#{current}'"
|
273
|
+
end
|
274
|
+
|
275
|
+
raise GemNotFound, message
|
276
|
+
else
|
277
|
+
raise GemNotFound, "Could not find gem '#{current}' in any of the sources."
|
278
|
+
end
|
279
|
+
location = current.source ? current.source.to_s : "any of the sources"
|
280
|
+
raise GemNotFound, "Could not find gem '#{current}' in #{location}.\\n"
|
281
|
+
else
|
282
|
+
@errors[current.name] = [nil, current]
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
matching_versions.reverse_each do |spec|
|
287
|
+
conflict = resolve_requirement(spec, current, reqs.dup, activated.dup)
|
288
|
+
conflicts << conflict if conflict
|
289
|
+
end
|
290
|
+
# If the current requirement is a root level gem and we have conflicts, we
|
291
|
+
# can figure out the best spot to backtrack to.
|
292
|
+
if current.required_by.empty? && !conflicts.empty?
|
293
|
+
# Check the current "catch" stack for the first one that is included in the
|
294
|
+
# conflicts set. That is where the parent of the conflicting gem was required.
|
295
|
+
# By jumping back to this spot, we can try other version of the parent of
|
296
|
+
# the conflicting gem, hopefully finding a combination that activates correctly.
|
297
|
+
@stack.reverse_each do |savepoint|
|
298
|
+
if conflicts.include?(savepoint)
|
299
|
+
debug { " -> Jumping to: #{savepoint}" }
|
300
|
+
throw savepoint
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def resolve_requirement(spec, requirement, reqs, activated)
|
308
|
+
# We are going to try activating the spec. We need to keep track of stack of
|
309
|
+
# requirements that got us to the point of activating this gem.
|
310
|
+
spec.required_by.replace requirement.required_by
|
311
|
+
spec.required_by << requirement
|
312
|
+
|
313
|
+
activated[spec.name] = spec
|
314
|
+
debug { " Activating: #{spec.name} (#{spec.version})" }
|
315
|
+
debug { spec.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\\n") }
|
316
|
+
|
317
|
+
# Now, we have to loop through all child dependencies and add them to our
|
318
|
+
# array of requirements.
|
319
|
+
debug { " Dependencies"}
|
320
|
+
spec.dependencies.each do |dep|
|
321
|
+
next if dep.type == :development
|
322
|
+
debug { " * #{dep.name} (#{dep.requirement})" }
|
323
|
+
dep.required_by.replace(requirement.required_by)
|
324
|
+
dep.required_by << requirement
|
325
|
+
reqs << dep
|
326
|
+
end
|
327
|
+
|
328
|
+
# We create a savepoint and mark it by the name of the requirement that caused
|
329
|
+
# the gem to be activated. If the activated gem ever conflicts, we are able to
|
330
|
+
# jump back to this point and try another version of the gem.
|
331
|
+
length = @stack.length
|
332
|
+
@stack << requirement.name
|
333
|
+
retval = catch(requirement.name) do
|
334
|
+
resolve(reqs, activated)
|
335
|
+
end
|
336
|
+
# Since we're doing a lot of throw / catches. A push does not necessarily match
|
337
|
+
# up to a pop. So, we simply slice the stack back to what it was before the catch
|
338
|
+
# block.
|
339
|
+
@stack.slice!(length..-1)
|
340
|
+
retval
|
341
|
+
end
|
342
|
+
|
343
|
+
def search(dep)
|
344
|
+
index = @source_requirements[dep.name] || @index
|
345
|
+
index.search(dep)
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
end
|
@@ -63,6 +63,10 @@ module Sprout
|
|
63
63
|
# archive_path: flickr-.87/src
|
64
64
|
# EOF
|
65
65
|
attr_writer :sprout_spec
|
66
|
+
# An array of library names this sprout depends on.
|
67
|
+
# Each will be expanded to sprout-[library-name]-library
|
68
|
+
# and resolved at compile stage
|
69
|
+
attr_writer :libraries
|
66
70
|
|
67
71
|
def self.define_task(args, &block)
|
68
72
|
t = super
|
@@ -110,6 +114,10 @@ module Sprout
|
|
110
114
|
gem_dependencies.each do |dep|
|
111
115
|
s.requirements << dep
|
112
116
|
end
|
117
|
+
libraries.each do |l|
|
118
|
+
puts "Adding #{l} to gem dependencies"
|
119
|
+
s.add_dependency("sprout-#{l}-library", ">= 0.0.0")
|
120
|
+
end
|
113
121
|
|
114
122
|
sprout_requirement = s.requirements.collect do |req|
|
115
123
|
(req[0] == 'sprout')
|
@@ -157,6 +165,10 @@ module Sprout
|
|
157
165
|
return @extensions ||= []
|
158
166
|
end
|
159
167
|
|
168
|
+
def libraries
|
169
|
+
@libraries ||= []
|
170
|
+
end
|
171
|
+
|
160
172
|
private
|
161
173
|
|
162
174
|
def gem_package
|
data/lib/sprout/version.rb
CHANGED
data/lib/sprout.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sprout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.237
|
5
5
|
platform: mswin32
|
6
6
|
authors:
|
7
7
|
- Luke Bayes
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-03-
|
12
|
+
date: 2010-03-10 00:00:00 -08:00
|
13
13
|
default_executable: sprout
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -121,6 +121,7 @@ files:
|
|
121
121
|
- lib/progress_bar.rb
|
122
122
|
- lib/sprout/archive_unpacker.rb
|
123
123
|
- lib/sprout/builder.rb
|
124
|
+
- lib/sprout/bundle_resolver.rb
|
124
125
|
- lib/sprout/commands/generate.rb
|
125
126
|
- lib/sprout/dynamic_accessors.rb
|
126
127
|
- lib/sprout/general_tasks.rb
|