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

#initialize(sandbox, podfile, locked_dependencies = []) ⇒ Resolver

Returns a new instance of Resolver

Parameters:

  • sandbox (Sandbox)

    @see sandbox

  • podfile (Podfile)

    @see podfile

  • locked_dependencies (Array<Dependency>) (defaults to: [])

    @see 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

#cached_setsHash<String => Set> (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.

Returns:

  • (Hash<String => Set>)

    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

#cached_sourcesSource::Aggregate (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.

Returns:

  • (Source::Aggregate)

    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

#cached_specsHash<String => Specification> (private)

Returns The loaded specifications grouped by name.

Returns:

  • (Hash<String => Specification>)

    The loaded specifications grouped by name.



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

def cached_specs
  @cached_specs
end

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



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

def locked_dependencies
  @locked_dependencies
end

#podfilePodfile (readonly)

Returns the Podfile used by the resolver.

Returns:

  • (Podfile)

    the Podfile used by the resolver.



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

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.



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

def sandbox
  @sandbox
end

#specs_by_targetHash{Podfile::TargetDefinition => Array<Specification>} (readonly)

Note:

The returned specifications can be subspecs.

Returns the resolved specifications grouped by target.

Returns:



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

def specs_by_target
  @specs_by_target
end

Instance Method Details

#find_cached_set(dependency) ⇒ Set (private)

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

Parameters:

  • dependency (Dependency)

    the dependency for which the set is needed.

Returns:

  • (Set)

    the cached set for a 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

#find_dependency_specs(dependent_spec, dependencies, target_definition) ⇒ void (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.

Parameters:

  • dependent_spec (Podfile, Specification, #to_s)

    the specification whose dependencies are being resolved. Used only for UI purposes.

  • dependencies (Array<Dependency>)

    the dependencies of the specification.

  • target_definition (TargetDefinition)

    the target definition that owns the specification.



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

#resolveHash{TargetDefinition => Array<Specification>}

Identifies the specifications that should be installed.

Returns:

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

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



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

#validate_platform(spec, target) ⇒ void (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