Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Use the Lockfile's specs as LazySpecifications instead of converting …

…them to Gem Dependencies.
  • Loading branch information...
commit cf736cf6055a5f08db05a41aff872bd9cbf0ac8d 1 parent 0ffe385
Carl Lerche authored
3  TODO.md
View
@@ -7,3 +7,6 @@
- bundle irb / bundle ruby / bundle [whatever] -> bundle exec
- Make bundle (install) work when sudo might be needed
- Generate a bundle stub into the application
+
+ - SpecSet#for should be smart about platforms since it could
+ have invalid platforms in the set.
3  lib/bundler.rb
View
@@ -50,6 +50,9 @@ class DeprecatedOption < BundlerError; status_code(12) ; end
class GemspecError < BundlerError; status_code(14) ; end
class InvalidOption < BundlerError; status_code(15) ; end
+ # Internal errors, should be rescued
+ class InvalidSpecSet < StandardError; end
+
class << self
attr_writer :ui, :bundle_path
14 lib/bundler/cli.rb
View
@@ -131,12 +131,14 @@ def update(*gems)
sources = Array(options[:source])
if gems.empty? && sources.empty?
- gems = Bundler.definition.locked_specs.map { |s| s.name }
- end
-
- Installer.install Bundler.root, Bundler.definition do |i|
- i.unlock_gems gems
- i.unlock_sources sources
+ # We're doing a full update
+ FileUtils.rm Bundler.root.join("Gemfile.lock")
+ Installer.install Bundler.root, Bundler.definition
+ else
+ Installer.install Bundler.root, Bundler.definition do |i|
+ i.unlock_gems gems
+ i.unlock_sources sources
+ end
end
end
50 lib/bundler/definition.rb
View
@@ -18,6 +18,18 @@ def self.build(gemfile, lockfile)
builder.to_definition(lockfile)
end
+=begin
+ How does the new system work?
+ ===
+ * Load information from Gemfile and Lockfile
+ * Invalidate stale locked specs
+ * All specs from stale source are stale
+ * All specs that are reachable only through a stale
+ dependency are stale.
+ * If all fresh dependencies are satisfied by the locked
+ specs, then we can try to resolve locally.
+=end
+
def initialize(lockfile, dependencies, sources)
@dependencies, @sources, @unlock = dependencies, sources, []
@@ -108,8 +120,9 @@ def to_lock
# Add the current platform
platforms = @platforms.dup
- platforms << Gem::Platform.local
- platforms.uniq!
+ platforms << Gem::Platform.local unless @platforms.any? do |p|
+ p == Gem::Platform.local
+ end
platforms.map { |p| p.to_s }.sort.each do |p|
out << " #{p}\n"
@@ -132,14 +145,18 @@ def to_lock
def converge
common = @locked_sources & @sources
fresh = @sources - common
- stale = @locked_sources - common
+ # stale = @locked_sources - common
+
+ @sources = common + fresh
@locked_specs.each do |s|
- next unless stale.include?(s.source)
- @unlock << s.name
+ if source = @sources.find { |source| s.source == source }
+ s.source = source
+ else
+ @unlock << s.name
+ end
end
- @sources = common + fresh
@dependencies.each do |dep|
if dep.source && source = @sources.find { |s| dep.source == s }
dep.source == source
@@ -192,28 +209,17 @@ def resolve(type, idx)
# Run a resolve against the locally available gems
specs = Resolver.resolve(dependencies, idx, source_requirements, locked_specs)
- specs.each do |spec|
- next unless spec.is_a?(LazySpecification)
+ specs.__materialize__ do |spec|
spec.__materialize__(spec.source.send(type))
end
specs
end
+ # TODO: Improve this logic
def resolve_remote_specs
- # An ambiguous dependency is any dependency that does not have
- # a requirement on an explicit version. If there are any, then
- # we must do a remote resolve.
- if dependencies.any? { |d| ambiguous?(d) }
- return resolve(:specs, remote_index)
- end
-
- # Simple logic for now. Can improve later.
- if specs.length == dependencies.length
- return specs
- else
- return resolve(:specs, remote_index)
- end
- rescue GemNotFound, PathError => e
+ locked_specs.for(dependencies) # Will raise on fail
+ specs
+ rescue #InvalidSpecSet, GemNotFound, PathError
resolve(:specs, remote_index)
end
3  lib/bundler/lazy_specification.rb
View
@@ -29,6 +29,7 @@ def satisfies?(dependency)
def __materialize__(index)
@specification = index.search(self).first
raise "Could not materialize #{full_name}" unless @specification
+ @specification
end
def respond_to?(*args)
@@ -39,7 +40,7 @@ def respond_to?(*args)
def method_missing(method, *args, &blk)
if Gem::Specification.new.respond_to?(method)
- raise "LazySpecification has not been materialized yet" unless @specification
+ raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
@specification.send(method, *args, &blk)
else
super
54 lib/bundler/resolver.rb
View
@@ -40,23 +40,7 @@ def self.resolve(requirements, index, source_requirements = {}, base = [])
activated = {}
base.each { |s| activated[s.name] = s }
resolver.resolve(requirements, activated)
- output = resolver.errors.inject("") do |o, (conflict, (origin, requirement))|
- if origin
- o << " Conflict on: #{conflict.inspect}:\n"
- o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\n"
- o << " * #{requirement} required"
- if requirement.required_by.first
- o << " by #{requirement.required_by.first}\n"
- else
- o << " in Gemfile\n"
- end
- else
- o << " #{requirement} not found in any of the sources\n"
- o << " required by #{requirement.required_by.first}\n"
- end
- o << " All possible versions of origin requirements conflict."
- end
- raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{output}"
+ raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{resolver.error_message}"
nil
end
SpecSet.new(result.values)
@@ -131,11 +115,20 @@ def resolve(reqs, activated)
# of it (maybe the current requirement won't be present anymore). If the
# current requirement is a root level requirement, we need to jump back to
# where the conflicting gem was activated.
- parent = current.required_by.last || existing.required_by.last
+ parent = current.required_by.last
+ # `existing` could not respond to required_by if it is part of the base set
+ # of specs that was passed to the resolver (aka, instance of LazySpecification)
+ parent ||= existing.required_by.last if existing.respond_to?(:required_by)
# We track the spot where the current gem was activated because we need
# to keep a list of every spot a failure happened.
debug { " -> Jumping to: #{parent.name}" }
- throw parent.name, existing.required_by.last.name
+ if parent
+ throw parent.name, existing.respond_to?(:required_by) && existing.required_by.last.name
+ else
+ # The original set of dependencies conflict with the base set of specs
+ # passed to the resolver. This is by definition an impossible resolve.
+ raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{error_message}"
+ end
end
else
# There are no activated gems for the current requirement, so we are going
@@ -236,5 +229,28 @@ def search(dep)
index = @source_requirements[dep.name] || @index
index.search(dep)
end
+
+ def error_message
+ output = errors.inject("") do |o, (conflict, (origin, requirement))|
+ if origin
+ o << " Conflict on: #{conflict.inspect}:\n"
+ if origin.respond_to?(:required_by) && required_by = origin.required_by.first
+ o << " * #{conflict} (#{origin.version}) activated by #{required_by}\n"
+ else
+ o << " * #{conflict} (#{origin.version}) in Gemfile.lock\n"
+ end
+ o << " * #{requirement} required"
+ if requirement.required_by.first
+ o << " by #{requirement.required_by.first}\n"
+ else
+ o << " in Gemfile\n"
+ end
+ else
+ o << " #{requirement} not found in any of the sources\n"
+ o << " required by #{requirement.required_by.first}\n"
+ end
+ o << " All possible versions of origin requirements conflict."
+ end
+ end
end
end
18 lib/bundler/spec_set.rb
View
@@ -16,6 +16,7 @@ def length
@specs.length
end
+ # TODO: Handle platform filtering
def for(deps, skip = [])
specs = {}
deps.each do |dep|
@@ -23,13 +24,21 @@ def for(deps, skip = [])
append_subgraph(specs, current, skip)
end
- sorted.select { |s| specs[s.name] }
+ SpecSet.new(sorted.select { |s| specs[s.name] })
end
def to_a
sorted.dup
end
+ def __materialize__
+ @lookup = nil
+ @specs.map! do |s|
+ next s unless s.is_a?(LazySpecification)
+ yield s
+ end
+ end
+
private
def append_subgraph(specs, current, skip)
@@ -42,12 +51,15 @@ def append_subgraph(specs, current, skip)
end
def sorted
- @sorted ||= ([lookup['rake']] + tsort).compact.uniq
+ rake = @specs.find { |s| s.name == 'rake' }
+ @sorted ||= ([rake] + tsort).compact.uniq
end
def lookup
@lookup ||= Hash.new do |h,k|
- h[k] = @specs.find { |s| s.name == k }
+ v = @specs.find { |s| s.name == k }
+ raise InvalidSpecSet, "SpecSet is missing '#{k}'" unless v
+ h[k] = v
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.