aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bundler
diff options
context:
space:
mode:
authorCarl Lerche <carllerche@mac.com>2010-06-02 12:51:46 -0700
committerCarl Lerche <carllerche@mac.com>2010-06-02 12:51:46 -0700
commitcf736cf6055a5f08db05a41aff872bd9cbf0ac8d (patch)
tree2b5398427b9e7ec2401bb0af0f02a4dd4a780ff4 /lib/bundler
parent0ffe385f5889d3cb45dfd1d7cb4d95ad8e786df3 (diff)
downloadbundler-cf736cf6055a5f08db05a41aff872bd9cbf0ac8d.tar.gz
Use the Lockfile's specs as LazySpecifications instead of converting them to Gem Dependencies.
Diffstat (limited to 'lib/bundler')
-rw-r--r--lib/bundler/cli.rb14
-rw-r--r--lib/bundler/definition.rb50
-rw-r--r--lib/bundler/lazy_specification.rb3
-rw-r--r--lib/bundler/resolver.rb54
-rw-r--r--lib/bundler/spec_set.rb18
5 files changed, 88 insertions, 51 deletions
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index fe3b2667..51ef6b7a 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -131,12 +131,14 @@ module Bundler
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
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index b332f26c..b78b1c91 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -18,6 +18,18 @@ module Bundler
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 @@ module Bundler
# 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 @@ module Bundler
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 @@ module Bundler
# 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
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index fde443dd..25349c14 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -29,6 +29,7 @@ module Bundler
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 @@ module Bundler
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
diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb
index aa39a6ad..9cbb0167 100644
--- a/lib/bundler/resolver.rb
+++ b/lib/bundler/resolver.rb
@@ -40,23 +40,7 @@ module Bundler
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 @@ module Bundler
# 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 @@ module Bundler
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
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index a8e8a5fc..ab53c7d2 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -16,6 +16,7 @@ module Bundler
@specs.length
end
+ # TODO: Handle platform filtering
def for(deps, skip = [])
specs = {}
deps.each do |dep|
@@ -23,13 +24,21 @@ module Bundler
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 @@ module Bundler
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