Commit a92c46a7 authored by Samuel E. Giddins's avatar Samuel E. Giddins

[Analyzer][Resolver] Implement more complete dependency locking

Tis should now behave more like Bundler
parent 39e51480
......@@ -149,17 +149,13 @@ module Pod
pods_state = nil
UI.section 'Finding Podfile changes' do
pods_by_state = lockfile.detect_changes_with_podfile(podfile)
pods_by_state.dup.each do |state, full_names|
pods = full_names.map { |fn| Specification.root_name(fn) }.uniq
pods_by_state[state] = pods
end
pods_state = SpecsState.new(pods_by_state)
pods_state.print
end
pods_state
else
state = SpecsState.new
state.added.concat(podfile.dependencies.map(&:root_name).uniq)
state.added.concat(podfile.dependencies.map(&:name).uniq)
state
end
end
......@@ -231,22 +227,45 @@ module Pod
# is in update mode, to prevent it from upgrading the Pods that weren't
# changed in the {Podfile}.
#
# @return [Array<Dependency>] the dependencies generate by the lockfile
# that prevent the resolver to update a Pod.
# @return [Molinillo::DependencyGraph<Dependency>] the dependencies
# generated by the lockfile that prevent the resolver to update
# a Pod.
#
def generate_version_locking_dependencies
if update_mode == :all
[]
else
locking_pods = result.podfile_state.unchanged
require 'molinillo/dependency_graph'
dependency_graph = Molinillo::DependencyGraph.new
unless update_mode == :all || !lockfile
explicit_dependencies = lockfile.to_hash['DEPENDENCIES'] || []
explicit_dependencies.each do |string|
dependency = Dependency.new(string)
dependency_graph.add_root_vertex(dependency.name, nil)
end
add_to_dependency_graph = lambda do |object, parents|
case object
when String
dependency = Dependency.from_string(object)
dependency_graph.add_child_vertex(dependency.name, parents.empty? ? dependency : nil, parents, nil)
when Hash
object.each do |key, value|
dependency = Dependency.from_string(key)
dependency_graph.add_child_vertex(dependency.name, parents.empty? ? dependency : nil, parents, nil)
value.each { |v| add_to_dependency_graph.call(v, [dependency.name]) }
end
end
end
pods = lockfile.to_hash['PODS'] || []
pods.each do |p|
add_to_dependency_graph.call(p, [])
end
pods_to_update = result.podfile_state.changed + result.podfile_state.deleted
if update_mode == :selected
# If selected Pods should been updated, filter them out of the list
locking_pods = locking_pods.reject { |pod| update[:pods].include?(pod) }
pods_to_update += update[:pods]
end
locking_pods.map do |pod|
lockfile.dependencies_to_lock_pod_named(pod)
end.flatten
pods_to_update.each { |u| dependency_graph.detach_vertex_named(u) }
end
dependency_graph
end
# Fetches the podspecs of external sources if modifications to the
......@@ -281,9 +300,9 @@ module Pod
if update_mode == :selected
pods_to_fetch += update[:pods]
end
deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch.include?(dep.root_name) }
deps_to_fetch_if_needed = deps_with_external_source.select { |dep| result.podfile_state.unchanged.include?(dep.root_name) }
deps_to_fetch += deps_to_fetch_if_needed.select { |dep| sandbox.specification(dep.root_name).nil? || !dep.external_source[:local].nil? || !dep.external_source[:path].nil? || !sandbox.pod_dir(dep.root_name).directory? }
deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch.include?(dep.name) }
deps_to_fetch_if_needed = deps_with_external_source.select { |dep| result.podfile_state.unchanged.include?(dep.name) }
deps_to_fetch += deps_to_fetch_if_needed.select { |dep| sandbox.specification(dep.name).nil? || !dep.external_source[:local].nil? || !dep.external_source[:path].nil? || !sandbox.pod_dir(dep.name).directory? }
end
unless deps_to_fetch.empty?
......@@ -352,7 +371,7 @@ module Pod
# @!group Analysis internal products
# @return [Array<Dependency>] the dependencies generate by the lockfile
# @return [Molinillo::DependencyGraph<Dependency>] the dependencies generated by the lockfile
# that prevent the resolver to update a Pod.
#
attr_reader :locked_dependencies
......@@ -604,7 +623,7 @@ module Pod
#
class SpecsState
# @param [Hash{Symbol=>String}] pods_by_state
# The **root** name of the pods grouped by their state
# The name of the pods grouped by their state
# (`:added`, `:removed`, `:changed` or `:unchanged`).
#
def initialize(pods_by_state = nil)
......@@ -656,12 +675,9 @@ module Pod
# @param [Symbol]
# the state of the Pod.
#
# @raise If there is an attempt to add the name of a subspec.
#
# @return [void]
#
def add_name(name, state)
raise '[Bug] Attempt to add subspec to the pods state' if name.include?('/')
send(state) << name
end
end
......
......@@ -51,11 +51,8 @@ module Pod
#
def resolve
dependencies = @podfile.target_definition_list.map(&:dependencies).flatten
base = locked_dependencies.reduce(Molinillo::DependencyGraph.new) do |graph, locked|
graph.tap { |g| g.add_root_vertex(locked.name, locked) }
end
@cached_sets = {}
@activated = Molinillo::Resolver.new(self, self).resolve(dependencies, base)
@activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
specs_by_target
rescue Molinillo::ResolverError => e
raise Informative, e.message
......
......@@ -219,6 +219,7 @@ module Pod
# @return [Nil] if the podspec is not stored.
#
def specification_path(name)
name = Specification.root_name(name)
path = specifications_root + "#{name}.podspec"
if path.exist?
path
......@@ -378,7 +379,7 @@ module Pod
end
# @return [Hash{String=>String}] The path of the Pods with a local source
# grouped by their name.
# grouped by their root name.
#
# @todo Rename (e.g. `pods_with_local_path`)
#
......
......@@ -13,7 +13,7 @@ def create_analyzer
end
hash = {}
hash['PODS'] = ['JSONKit (1.4)', 'NUI (0.2.0)', 'SVPullToRefresh (0.4)']
hash['PODS'] = ['JSONKit (1.5pre)', 'NUI (0.2.0)', 'SVPullToRefresh (0.4)']
hash['DEPENDENCIES'] = %w(JSONKit NUI SVPullToRefresh)
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
......@@ -52,10 +52,10 @@ module Pod
it 'computes the state of the Podfile respect to the Lockfile' do
state = @analyzer.analyze.podfile_state
state.added.should == %w(AFNetworking libextobjc)
state.changed.should == ['JSONKit']
state.unchanged.should == ['SVPullToRefresh']
state.deleted.should == ['NUI']
state.added.should == %w(AFNetworking libextobjc/EXTKeyPathCoding)
state.changed.should == %w()
state.unchanged.should == %w(JSONKit SVPullToRefresh)
state.deleted.should == %w(NUI)
end
#--------------------------------------#
......@@ -131,13 +131,35 @@ module Pod
it 'locks the version of the dependencies which did not change in the Podfile' do
@analyzer.analyze
@analyzer.send(:locked_dependencies).map(&:to_s).should == ['SVPullToRefresh (= 0.4)']
@analyzer.send(:locked_dependencies).map(&:payload).map(&:to_s).
should == ['JSONKit (= 1.5pre)', 'SVPullToRefresh (= 0.4)']
end
it 'does not lock the dependencies in update mode' do
@analyzer.update = true
@analyzer.analyze
@analyzer.send(:locked_dependencies).map(&:to_s).should == []
@analyzer.send(:locked_dependencies).to_a.map(&:payload).should == []
end
#--------------------------------------#
it 'takes into account locked implicit dependencies' do
podfile = Podfile.new do
platform :ios, '8.0'
xcodeproj 'SampleProject/SampleProject'
pod 'ARAnalytics/Mixpanel'
end
hash = {}
hash['PODS'] = ['ARAnalytics/CoreIOS (2.8.0)', {'ARAnalytics/Mixpanel (2.8.0)' => ['ARAnlytics/CoreIOS', 'Mixpanel']}, 'Mixpanel (2.5.1)']
hash['DEPENDENCIES'] = %w(ARAnalytics/Mixpanel)
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
lockfile = Pod::Lockfile.new(hash)
analyzer = Installer::Analyzer.new(config.sandbox, podfile, lockfile)
analyzer.analyze.specifications.
find { |s| s.name == 'Mixpanel' }.
version.to_s.should == '2.5.1'
end
#--------------------------------------#
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment