sprout 0.7.236-x86-darwin-10 → 0.7.237-x86-darwin-10

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.rb CHANGED
@@ -29,6 +29,7 @@ require 'sprout/archive_unpacker'
29
29
  require 'sprout/remote_file_target'
30
30
  require 'sprout/simple_resolver'
31
31
  # require 'sprout/template_resolver'
32
+ require 'sprout/bundle_resolver'
32
33
 
33
34
  require 'rubygems/installer'
34
35
  require 'rubygems/source_info_cache'
@@ -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
@@ -3,7 +3,7 @@ module Sprout
3
3
  module VERSION #:nodoc:
4
4
  MAJOR = 0
5
5
  MINOR = 7
6
- TINY = 236
6
+ TINY = 237
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  MAJOR_MINOR = [MAJOR, MINOR].join('.')
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.236
4
+ version: 0.7.237
5
5
  platform: x86-darwin-10
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-03 00:00:00 -08:00
12
+ date: 2010-03-10 00:00:00 -08:00
13
13
  default_executable: sprout
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -101,6 +101,7 @@ files:
101
101
  - lib/progress_bar.rb
102
102
  - lib/sprout/archive_unpacker.rb
103
103
  - lib/sprout/builder.rb
104
+ - lib/sprout/bundle_resolver.rb
104
105
  - lib/sprout/commands/generate.rb
105
106
  - lib/sprout/dynamic_accessors.rb
106
107
  - lib/sprout/general_tasks.rb