Class: Pod::Resolver

Inherits:
Object
  • Object
show all
Defined in:
lib/cocoapods/resolver.rb

Overview

TODO:

Its current implementation is naive, in the sense that it can't do full automatic resolves like Bundler: how-does-bundler-bundle

TODO:

Another limitation is that the order of the dependencies matter. The current implementation could create issues, for example, if a specification is loaded for a target definition and later for another target is set in head mode. The first specification will not be in head mode.

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

Resolution (collapse)

Instance Attribute Summary (collapse)

Resolution (collapse)

Private helpers (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Resolver) initialize(sandbox, podfile, locked_dependencies = [])



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

def initialize(sandbox, podfile, locked_dependencies = [])
  @sandbox = sandbox
  @podfile = podfile
  @locked_dependencies = locked_dependencies
end

Instance Attribute Details

- (Hash<String => Set>) cached_sets (private)

Note:

Sets store the resolved dependencies and return the highest available specification found in the sources. This is done globally and not per target definition because there can be just one Pod installation, so different version of the same Pods for target definitions are not allowed.

Returns A cache that keeps tracks of the sets loaded by the resolution process.



105
106
107
# File 'lib/cocoapods/resolver.rb', line 105

def cached_sets
  @cached_sets
end

- (Source::Aggregate) cached_sources (private)

Note:

The sources are cached because frequently accessed by the resolver and loading them requires disk activity.

Returns A cache of the sources needed to find the podspecs.



94
95
96
# File 'lib/cocoapods/resolver.rb', line 94

def cached_sources
  @cached_sources
end

- (Hash<String => Specification>) cached_specs (private)



110
111
112
# File 'lib/cocoapods/resolver.rb', line 110

def cached_specs
  @cached_specs
end

- (Array<Dependency>) locked_dependencies (readonly)



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

def locked_dependencies
  @locked_dependencies
end

- (Podfile) podfile (readonly)



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

def podfile
  @podfile
end

- (Sandbox) sandbox (readonly)



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

def sandbox
  @sandbox
end

- (Hash{Podfile::TargetDefinition => Array<Specification>}) specs_by_target (readonly)

Note:

The returned specifications can be subspecs.

Returns the resolved specifications grouped by target.



80
81
82
# File 'lib/cocoapods/resolver.rb', line 80

def specs_by_target
  @specs_by_target
end

Instance Method Details

- (Set) find_cached_set(dependency) (private)

Loads or returns a previously initialized for the Pod of the given dependency.



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/cocoapods/resolver.rb', line 181

def find_cached_set(dependency)
  name = dependency.root_name
  unless cached_sets[name]
    if dependency.external_source
      spec = sandbox.specification(dependency.root_name)
      unless spec
        raise StandardError, "[Bug] Unable to find the specification for `#{dependency}`."
      end
      set = Specification::Set::External.new(spec)
    else
      set = cached_sources.search(dependency)
    end
    cached_sets[name] = set
    unless set
      raise Informative, "Unable to find a specification for `#{dependency}`."
    end
  end
  cached_sets[name]
end

- (void) find_dependency_specs(dependent_spec, dependencies, target_definition) (private)

Note:

If there is a locked dependency with the same name of a given dependency the locked one is used in place of the dependency of the specification. In this way it is possible to prevent the update of the version of installed pods not changed in the Podfile.

Note:

The recursive process checks if a dependency has already been loaded to prevent an infinite loop.

Note:

The set class merges all (of all the target definitions) the dependencies and thus it keeps track of whether it is in head mode or from an external source because Dependency#merge preserves this information.

This method returns an undefined value.

Resolves recursively the dependencies of a specification and stores them in the @cached_specs ivar.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/cocoapods/resolver.rb', line 147

def find_dependency_specs(dependent_spec, dependencies, target_definition)
  dependencies.each do |dependency|
    locked_dep = locked_dependencies.find { |ld| ld.name == dependency.name }
    dependency = locked_dep if locked_dep

    UI.message("- #{dependency}", '', 2) do
      set = find_cached_set(dependency)
      set.required_by(dependency, dependent_spec.to_s)

      unless @loaded_specs.include?(dependency.name)
        spec = set.specification.subspec_by_name(dependency.name)
        @loaded_specs << spec.name
        cached_specs[spec.name] = spec
        validate_platform(spec, target_definition)
        if dependency.head? || sandbox.head_pod?(spec.name)
          spec.version.head = true
          sandbox.store_head_pod(spec.name)
        end

        spec_dependencies = spec.all_dependencies(target_definition.platform)
        find_dependency_specs(spec, spec_dependencies, target_definition)
      end
    end
  end
end

- (Hash{TargetDefinition => Array<Specification>}) resolve

Identifies the specifications that should be installed.



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

def resolve
  @cached_sources  = SourcesManager.aggregate
  @cached_sets     = {}
  @cached_specs    = {}
  @specs_by_target = {}

  target_definitions = podfile.target_definition_list
  target_definitions.each do |target|
    UI.section "Resolving dependencies for target `#{target.name}' (#{target.platform})" do
      @loaded_specs = []
      find_dependency_specs(podfile, target.dependencies, target)
      specs = cached_specs.values_at(*@loaded_specs).sort_by(&:name)
      specs_by_target[target] = specs
    end
  end

  cached_specs.values.sort_by(&:name)
  specs_by_target
end

- (void) validate_platform(spec, target) (private)

This method returns an undefined value.

Ensures that a specification is compatible with the platform of a target.

Raises:

  • If the specification is not supported by the target.



207
208
209
210
211
212
213
# File 'lib/cocoapods/resolver.rb', line 207

def validate_platform(spec, target)
  unless spec.available_platforms.any? { |p| target.platform.supports?(p) }
    raise Informative, "The platform of the target `#{target.name}` "     \
      "(#{target.platform}) is not compatible with `#{spec}` which has "  \
      "a minimum requirement of #{spec.available_platforms.join(' - ')}."
  end
end