diff options
author | Carl Lerche <carllerche@mac.com> | 2010-06-02 12:51:46 -0700 |
---|---|---|
committer | Carl Lerche <carllerche@mac.com> | 2010-06-02 12:51:46 -0700 |
commit | cf736cf6055a5f08db05a41aff872bd9cbf0ac8d (patch) | |
tree | 2b5398427b9e7ec2401bb0af0f02a4dd4a780ff4 /lib/bundler | |
parent | 0ffe385f5889d3cb45dfd1d7cb4d95ad8e786df3 (diff) | |
download | bundler-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.rb | 14 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 50 | ||||
-rw-r--r-- | lib/bundler/lazy_specification.rb | 3 | ||||
-rw-r--r-- | lib/bundler/resolver.rb | 54 | ||||
-rw-r--r-- | lib/bundler/spec_set.rb | 18 |
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 |