Class: Pod::Resolver

Inherits:
Object
  • Object
show all
Includes:
Molinillo::SpecificationProvider, Molinillo::UI
Defined in:
lib/cocoapods/resolver.rb,
lib/cocoapods/resolver/resolver_specification.rb

Overview

The resolver is responsible of generating a list of specifications grouped by target for a given Podfile.

Defined Under Namespace

Classes: ResolverSpecification

Instance Attribute Summary collapse

Resolution collapse

Instance Method Summary collapse

Constructor Details

#initialize(sandbox, podfile, locked_dependencies, sources, specs_updated, podfile_dependency_cache: Installer::Analyzer::PodfileDependencyCache.from_podfile(podfile), sources_manager: Config.instance.sources_manager) ⇒ Resolver

Init a new Resolver



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/cocoapods/resolver.rb', line 56

def initialize(sandbox, podfile, locked_dependencies, sources, specs_updated,
               podfile_dependency_cache: Installer::Analyzer::PodfileDependencyCache.from_podfile(podfile),
               sources_manager: Config.instance.sources_manager)
  @sandbox = sandbox
  @podfile = podfile
  @locked_dependencies = locked_dependencies
  @sources = Array(sources)
  @specs_updated = specs_updated
  @podfile_dependency_cache = podfile_dependency_cache
  @sources_manager = sources_manager
  @platforms_by_dependency = Hash.new { |h, k| h[k] = [] }

  @cached_sets = {}
  @podfile_requirements_by_root_name = @podfile_dependency_cache.podfile_dependencies.group_by(&:root_name).each_value { |a| a.map!(&:requirement).freeze }.freeze
  @search = {}
  @validated_platforms = Set.new
end

Instance Attribute Details

#locked_dependenciesArray<Dependency> (readonly)



30
31
32
# File 'lib/cocoapods/resolver.rb', line 30

def locked_dependencies
  @locked_dependencies
end

#podfilePodfile (readonly)



25
26
27
# File 'lib/cocoapods/resolver.rb', line 25

def podfile
  @podfile
end

#sandboxSandbox (readonly)



21
22
23
# File 'lib/cocoapods/resolver.rb', line 21

def sandbox
  @sandbox
end

#sourcesArray<Source> (readonly)



35
36
37
# File 'lib/cocoapods/resolver.rb', line 35

def sources
  @sources
end

#sources_managerSource::Manager (readonly)



44
45
46
# File 'lib/cocoapods/resolver.rb', line 44

def sources_manager
  @sources_manager
end

#specs_updatedBoolean (readonly) Also known as: specs_updated?



39
40
41
# File 'lib/cocoapods/resolver.rb', line 39

def specs_updated
  @specs_updated
end

Instance Method Details

#after_resolutionVoid

Called after resolution ends.

Completely silence this, as we show nothing.



310
311
# File 'lib/cocoapods/resolver.rb', line 310

def after_resolution
end

#before_resolutionVoid

Called before resolution starts.

Completely silence this, as we show nothing.



301
302
# File 'lib/cocoapods/resolver.rb', line 301

def before_resolution
end

#dependencies_for(specification) ⇒ Array<Specification>

Returns the dependencies of ‘specification`.



176
177
178
179
180
181
182
183
184
185
# File 'lib/cocoapods/resolver.rb', line 176

def dependencies_for(specification)
  root_name = Specification.root_name(specification.name)
  specification.all_dependencies.map do |dependency|
    if dependency.root_name == root_name
      dependency.dup.tap { |d| d.specific_version = specification.version }
    else
      dependency
    end
  end
end

#indicate_progressVoid

Called during resolution to indicate progress.

Completely silence this, as we show nothing.



319
320
# File 'lib/cocoapods/resolver.rb', line 319

def indicate_progress
end

#name_for(dependency) ⇒ String

Returns the name for the given ‘dependency`.



194
195
196
# File 'lib/cocoapods/resolver.rb', line 194

def name_for(dependency)
  dependency.name
end

#name_for_explicit_dependency_sourceString



200
201
202
# File 'lib/cocoapods/resolver.rb', line 200

def name_for_explicit_dependency_source
  'Podfile'
end

#name_for_locking_dependency_sourceString



206
207
208
# File 'lib/cocoapods/resolver.rb', line 206

def name_for_locking_dependency_source
  'Podfile.lock'
end

#outputUserInterface

The UI object the resolver should use for displaying user-facing output.



291
292
293
# File 'lib/cocoapods/resolver.rb', line 291

def output
  UI
end

#requirement_satisfied_by?(requirement, activated, spec) ⇒ Boolean

Determines whether the given ‘requirement` is satisfied by the given `spec`, in the context of the current `activated` dependency graph.



223
224
225
226
227
228
229
# File 'lib/cocoapods/resolver.rb', line 223

def requirement_satisfied_by?(requirement, activated, spec)
  version = spec.version
  return false unless requirement.requirement.satisfied_by?(version)
  return false unless valid_possibility_version_for_root_name?(requirement, activated, spec)
  return false unless spec_is_platform_compatible?(activated, requirement, spec)
  true
end

#resolveHash{TargetDefinition => Array<ResolverSpecification>}

Identifies the specifications that should be installed.



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/cocoapods/resolver.rb', line 86

def resolve
  dependencies = @podfile_dependency_cache.target_definition_list.flat_map do |target|
    @podfile_dependency_cache.target_definition_dependencies(target).each do |dep|
      next unless target.platform
      @platforms_by_dependency[dep].push(target.platform)
    end
  end.uniq
  @platforms_by_dependency.each_value(&:uniq!)
  @activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
  resolver_specs_by_target
rescue Molinillo::ResolverError => e
  handle_resolver_error(e)
end

#resolver_specs_by_targetHash{Podfile::TargetDefinition => Array<ResolverSpecification>}

Note:

The returned specifications can be subspecs.

Returns the resolved specifications grouped by target.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/cocoapods/resolver.rb', line 105

def resolver_specs_by_target
  @resolver_specs_by_target ||= {}.tap do |resolver_specs_by_target|
    @podfile_dependency_cache.target_definition_list.each do |target|
      next if target.abstract? && !target.platform

      # can't use vertex.root? since that considers _all_ targets
      explicit_dependencies = @podfile_dependency_cache.target_definition_dependencies(target).map(&:name).to_set

      used_by_aggregate_target_by_spec_name = {}
      used_vertices_by_spec_name = {}

      # it's safe to make a single pass here since we iterate in topological order,
      # so all of the predecessors have been visited before we get to a node.
      # #tsort returns no-children vertices first, and we want them last (i.e. we want no-parent vertices first)
      @activated.tsort.reverse_each do |vertex|
        spec_name = vertex.name
        explicitly_included = explicit_dependencies.include?(spec_name)
        if explicitly_included || vertex.incoming_edges.any? { |edge| used_vertices_by_spec_name.key?(edge.origin.name) && edge_is_valid_for_target_platform?(edge, target.platform) }
          validate_platform(vertex.payload, target)
          used_vertices_by_spec_name[spec_name] = vertex
          used_by_aggregate_target_by_spec_name[spec_name] = vertex.payload.library_specification? &&
            (explicitly_included || vertex.predecessors.any? { |predecessor| used_by_aggregate_target_by_spec_name.fetch(predecessor.name, false) })
        end
      end

      resolver_specs_by_target[target] = used_vertices_by_spec_name.each_value.
        map do |vertex|
          payload = vertex.payload
          non_library = !used_by_aggregate_target_by_spec_name.fetch(vertex.name)
          spec_source = payload.respond_to?(:spec_source) && payload.spec_source
          ResolverSpecification.new(payload, non_library, spec_source)
        end.
        sort_by(&:name)
    end
  end
end

#search_for(dependency) ⇒ Array<Specification>

Returns (and caches) the specification that satisfy the given dependency.



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/cocoapods/resolver.rb', line 157

def search_for(dependency)
  @search[dependency] ||= begin
    additional_requirements = if locked_requirement = requirement_for_locked_pod_named(dependency.name)
                                [locked_requirement]
                              else
                                Array(@podfile_requirements_by_root_name[dependency.root_name])
                              end

    specifications_for_dependency(dependency, additional_requirements).freeze
  end
end

#sort_dependencies(dependencies, activated, conflicts) ⇒ Array<Dependency>

Sort dependencies so that the ones that are easiest to resolve are first. Easiest to resolve is (usually) defined by:

1) Is this dependency already activated?
2) How relaxed are the requirements?
3) Are there any conflicts for this dependency?
4) How many possibilities are there to satisfy this dependency?


266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/cocoapods/resolver.rb', line 266

def sort_dependencies(dependencies, activated, conflicts)
  dependencies.sort_by! do |dependency|
    name = name_for(dependency)
    [
      activated.vertex_named(name).payload ? 0 : 1,
      dependency.external_source ? 0 : 1,
      dependency.prerelease? ? 0 : 1,
      conflicts[name] ? 0 : 1,
      search_for(dependency).count,
    ]
  end
end