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

Parameters:

  • sandbox (Sandbox)

    @see sandbox

  • podfile (Podfile)

    @see podfile

  • locked_dependencies (Array<Dependency>)

    @see locked_dependencies

  • sources (Array<Source>, Source)

    @see sources

  • specs_updated (Boolean)

    @see specs_updated

  • podfile_dependency_cache (PodfileDependencyCache) (defaults to: Installer::Analyzer::PodfileDependencyCache.from_podfile(podfile))

    the podfile dependency cache to use within this 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)

Returns the list of dependencies locked to a specific version.

Returns:

  • (Array<Dependency>)

    the list of dependencies locked to a specific version.



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

def locked_dependencies
  @locked_dependencies
end

#podfilePodfile (readonly)

Returns the Podfile used by the resolver.

Returns:

  • (Podfile)

    the Podfile used by the resolver.



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

def podfile
  @podfile
end

#sandboxSandbox (readonly)

Returns the Sandbox used by the resolver to find external dependencies.

Returns:

  • (Sandbox)

    the Sandbox used by the resolver to find external dependencies.



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

def sandbox
  @sandbox
end

#sourcesArray<Source> (readonly)

Returns The list of the sources which will be used for the resolution.

Returns:

  • (Array<Source>)

    The list of the sources which will be used for the resolution.



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

def sources
  @sources
end

#sources_managerSource::Manager (readonly)

Returns the manager to use for dependency resolution.

Returns:



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

def sources_manager
  @sources_manager
end

#specs_updatedBoolean (readonly) Also known as: specs_updated?

Returns Whether the resolver has sources repositories up-to-date.

Returns:

  • (Boolean)

    Whether the resolver has sources repositories up-to-date.



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.

Returns:

  • (Void)


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.

Returns:

  • (Void)


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

def before_resolution
end

#dependencies_for(specification) ⇒ Array<Specification>

Returns the dependencies of ‘specification`.

Parameters:

  • specification (Specification)

    the specification whose own dependencies are being asked for.

Returns:

  • (Array<Specification>)

    all 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.

Returns:

  • (Void)


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

def indicate_progress
end

#name_for(dependency) ⇒ String

Returns the name for the given ‘dependency`.

Parameters:

  • dependency (Dependency)

    the dependency whose name is being queried.

Returns:

  • (String)

    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

Returns the user-facing name for a Podfile.

Returns:

  • (String)

    the user-facing name for a Podfile.



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

def name_for_explicit_dependency_source
  'Podfile'
end

#name_for_locking_dependency_sourceString

Returns the user-facing name for a Lockfile.

Returns:

  • (String)

    the user-facing name for a Lockfile.



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.

Returns:



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.

Parameters:

  • requirement (Dependency)

    the dependency in question.

  • activated (Molinillo::DependencyGraph)

    the current dependency graph in the resolution process.

  • spec (Specification)

    the specification in question.

Returns:

  • (Boolean)

    whether ‘requirement` is satisfied by `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.

Returns:

  • (Hash{TargetDefinition => Array<ResolverSpecification>})

    resolver_specs_by_target the resolved specifications that need to be installed grouped by target definition.



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.

Returns:

  • (Hash{Podfile::TargetDefinition => Array<ResolverSpecification>})

    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.

Parameters:

  • dependency (Dependency)

    the dependency that is being searched for.

Returns:

  • (Array<Specification>)

    the specifications 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?

Parameters:

  • dependencies (Array<Dependency>)

    the unsorted dependencies.

  • activated (Molinillo::DependencyGraph)

    the dependency graph of currently activated specs.

  • conflicts ({String => Array<Conflict>})

    the current conflicts.

Returns:

  • (Array<Dependency>)

    the sorted dependencies.



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