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 ...@@ -149,17 +149,13 @@ module Pod
pods_state = nil pods_state = nil
UI.section 'Finding Podfile changes' do UI.section 'Finding Podfile changes' do
pods_by_state = lockfile.detect_changes_with_podfile(podfile) 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 = SpecsState.new(pods_by_state)
pods_state.print pods_state.print
end end
pods_state pods_state
else else
state = SpecsState.new state = SpecsState.new
state.added.concat(podfile.dependencies.map(&:root_name).uniq) state.added.concat(podfile.dependencies.map(&:name).uniq)
state state
end end
end end
...@@ -231,22 +227,45 @@ module Pod ...@@ -231,22 +227,45 @@ module Pod
# is in update mode, to prevent it from upgrading the Pods that weren't # is in update mode, to prevent it from upgrading the Pods that weren't
# changed in the {Podfile}. # changed in the {Podfile}.
# #
# @return [Array<Dependency>] the dependencies generate by the lockfile # @return [Molinillo::DependencyGraph<Dependency>] the dependencies
# that prevent the resolver to update a Pod. # generated by the lockfile that prevent the resolver to update
# a Pod.
# #
def generate_version_locking_dependencies def generate_version_locking_dependencies
if update_mode == :all require 'molinillo/dependency_graph'
[] dependency_graph = Molinillo::DependencyGraph.new
else unless update_mode == :all || !lockfile
locking_pods = result.podfile_state.unchanged 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 update_mode == :selected
# If selected Pods should been updated, filter them out of the list # 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 end
locking_pods.map do |pod| pods_to_update.each { |u| dependency_graph.detach_vertex_named(u) }
lockfile.dependencies_to_lock_pod_named(pod)
end.flatten
end end
dependency_graph
end end
# Fetches the podspecs of external sources if modifications to the # Fetches the podspecs of external sources if modifications to the
...@@ -281,9 +300,9 @@ module Pod ...@@ -281,9 +300,9 @@ module Pod
if update_mode == :selected if update_mode == :selected
pods_to_fetch += update[:pods] pods_to_fetch += update[:pods]
end end
deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch.include?(dep.root_name) } 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.root_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.root_name).nil? || !dep.external_source[:local].nil? || !dep.external_source[:path].nil? || !sandbox.pod_dir(dep.root_name).directory? } 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 end
unless deps_to_fetch.empty? unless deps_to_fetch.empty?
...@@ -352,7 +371,7 @@ module Pod ...@@ -352,7 +371,7 @@ module Pod
# @!group Analysis internal products # @!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. # that prevent the resolver to update a Pod.
# #
attr_reader :locked_dependencies attr_reader :locked_dependencies
...@@ -604,7 +623,7 @@ module Pod ...@@ -604,7 +623,7 @@ module Pod
# #
class SpecsState class SpecsState
# @param [Hash{Symbol=>String}] pods_by_state # @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`). # (`:added`, `:removed`, `:changed` or `:unchanged`).
# #
def initialize(pods_by_state = nil) def initialize(pods_by_state = nil)
...@@ -656,12 +675,9 @@ module Pod ...@@ -656,12 +675,9 @@ module Pod
# @param [Symbol] # @param [Symbol]
# the state of the Pod. # the state of the Pod.
# #
# @raise If there is an attempt to add the name of a subspec.
#
# @return [void] # @return [void]
# #
def add_name(name, state) def add_name(name, state)
raise '[Bug] Attempt to add subspec to the pods state' if name.include?('/')
send(state) << name send(state) << name
end end
end end
......
...@@ -51,11 +51,8 @@ module Pod ...@@ -51,11 +51,8 @@ module Pod
# #
def resolve def resolve
dependencies = @podfile.target_definition_list.map(&:dependencies).flatten 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 = {} @cached_sets = {}
@activated = Molinillo::Resolver.new(self, self).resolve(dependencies, base) @activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
specs_by_target specs_by_target
rescue Molinillo::ResolverError => e rescue Molinillo::ResolverError => e
raise Informative, e.message raise Informative, e.message
......
...@@ -219,6 +219,7 @@ module Pod ...@@ -219,6 +219,7 @@ module Pod
# @return [Nil] if the podspec is not stored. # @return [Nil] if the podspec is not stored.
# #
def specification_path(name) def specification_path(name)
name = Specification.root_name(name)
path = specifications_root + "#{name}.podspec" path = specifications_root + "#{name}.podspec"
if path.exist? if path.exist?
path path
...@@ -378,7 +379,7 @@ module Pod ...@@ -378,7 +379,7 @@ module Pod
end end
# @return [Hash{String=>String}] The path of the Pods with a local source # @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`) # @todo Rename (e.g. `pods_with_local_path`)
# #
......
...@@ -13,7 +13,7 @@ def create_analyzer ...@@ -13,7 +13,7 @@ def create_analyzer
end end
hash = {} 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['DEPENDENCIES'] = %w(JSONKit NUI SVPullToRefresh)
hash['SPEC CHECKSUMS'] = {} hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION hash['COCOAPODS'] = Pod::VERSION
...@@ -52,10 +52,10 @@ module Pod ...@@ -52,10 +52,10 @@ module Pod
it 'computes the state of the Podfile respect to the Lockfile' do it 'computes the state of the Podfile respect to the Lockfile' do
state = @analyzer.analyze.podfile_state state = @analyzer.analyze.podfile_state
state.added.should == %w(AFNetworking libextobjc) state.added.should == %w(AFNetworking libextobjc/EXTKeyPathCoding)
state.changed.should == ['JSONKit'] state.changed.should == %w()
state.unchanged.should == ['SVPullToRefresh'] state.unchanged.should == %w(JSONKit SVPullToRefresh)
state.deleted.should == ['NUI'] state.deleted.should == %w(NUI)
end end
#--------------------------------------# #--------------------------------------#
...@@ -131,13 +131,35 @@ module Pod ...@@ -131,13 +131,35 @@ module Pod
it 'locks the version of the dependencies which did not change in the Podfile' do it 'locks the version of the dependencies which did not change in the Podfile' do
@analyzer.analyze @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 end
it 'does not lock the dependencies in update mode' do it 'does not lock the dependencies in update mode' do
@analyzer.update = true @analyzer.update = true
@analyzer.analyze @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 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