mirror of
https://github.com/zhigang1992/CocoaPods.git
synced 2026-01-12 17:12:54 +08:00
223 lines
7.8 KiB
Ruby
223 lines
7.8 KiB
Ruby
require 'colored'
|
|
|
|
module Pod
|
|
class Resolver
|
|
include Config::Mixin
|
|
|
|
# @return [Bool] Whether the resolver should find the pods to install or
|
|
# the pods to update.
|
|
#
|
|
attr_accessor :update_mode
|
|
|
|
# @return [Bool] Whether the resolver should update the external specs
|
|
# in the resolution process.
|
|
#
|
|
attr_accessor :update_external_specs
|
|
|
|
# @return [Podfile] The Podfile used by the resolver.
|
|
#
|
|
attr_reader :podfile
|
|
|
|
# @return [Lockfile] The Lockfile used by the resolver.
|
|
#
|
|
attr_reader :lockfile
|
|
|
|
# @return [Sandbox] The Sandbox used by the resolver to find external
|
|
# dependencies.
|
|
#
|
|
attr_reader :sandbox
|
|
|
|
# @return [Array<Strings>] The name of the pods that have an
|
|
# external source.
|
|
#
|
|
attr_reader :pods_from_external_sources
|
|
|
|
# @return [Array<Set>] A cache of the sets used to resolve the dependencies.
|
|
#
|
|
attr_reader :cached_sets
|
|
|
|
# @return [Source::Aggregate] A cache of the sources needed to find the
|
|
# podspecs.
|
|
#
|
|
attr_reader :cached_sources
|
|
|
|
# @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
|
|
# Returns the resolved specifications grouped by target.
|
|
#
|
|
attr_reader :specs_by_target
|
|
|
|
def initialize(podfile, lockfile, sandbox)
|
|
@podfile = podfile
|
|
@lockfile = lockfile
|
|
@sandbox = sandbox
|
|
@update_external_specs = true
|
|
|
|
@cached_sets = {}
|
|
@cached_sources = Source::Aggregate.new
|
|
end
|
|
|
|
# Identifies the specifications that should be installed according whether
|
|
# the resolver is in update mode or not.
|
|
#
|
|
# @return [Hash{Podfile::TargetDefinition => Array<Specification>}] specs_by_target
|
|
#
|
|
def resolve
|
|
@cached_specs = {}
|
|
@specs_by_target = {}
|
|
@pods_from_external_sources = []
|
|
@pods_to_lock = []
|
|
|
|
if @lockfile
|
|
@pods_by_state = @lockfile.detect_changes_with_podfile(podfile)
|
|
UI.section "Finding added, modified or removed dependencies:" do
|
|
marks = {:added => "A".green, :changed => "M".yellow, :removed => "R".red, :unchanged => "-" }
|
|
@pods_by_state.each do |symbol, pod_names|
|
|
pod_names.each do |pod_name|
|
|
UI.message("#{marks[symbol]} #{pod_name}", '',2)
|
|
end
|
|
end
|
|
end if config.verbose?
|
|
@pods_to_lock = (lockfile.pods_names - @pods_by_state[:added] - @pods_by_state[:changed] - @pods_by_state[:removed]).uniq
|
|
end
|
|
|
|
unless config.skip_repo_update?
|
|
UI.section 'Updating spec repositories' do
|
|
Command::Repo.new(Command::ARGV.new(["update"])).run
|
|
end if !@lockfile || !(@pods_by_state[:added] + @pods_by_state[:changed]).empty? || update_mode
|
|
end
|
|
|
|
@podfile.target_definitions.values.each do |target_definition|
|
|
UI.section "Resolving dependencies for target `#{target_definition.name}' (#{target_definition.platform})" do
|
|
@loaded_specs = []
|
|
find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
|
|
@specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
|
|
end
|
|
end
|
|
|
|
@cached_specs.values.sort_by(&:name)
|
|
@specs_by_target
|
|
end
|
|
|
|
# @return [Array<Specification>] The specifications loaded by the resolver.
|
|
#
|
|
def specs
|
|
@cached_specs.values.uniq
|
|
end
|
|
|
|
# @return [Bool] Whether a pod should be installed/reinstalled.
|
|
#
|
|
def should_install?(name)
|
|
pods_to_install.include? name
|
|
end
|
|
|
|
# @return [Array<Strings>] The name of the pods that should be
|
|
# installed/reinstalled.
|
|
#
|
|
def pods_to_install
|
|
unless @pods_to_install
|
|
if lockfile
|
|
@pods_to_install = specs.select do |spec|
|
|
spec.version != lockfile.pods_versions[spec.pod_name]
|
|
end.map(&:name)
|
|
if update_mode
|
|
@pods_to_install += specs.select do |spec|
|
|
spec.version.head? || pods_from_external_sources.include?(spec.pod_name)
|
|
end.map(&:name)
|
|
end
|
|
@pods_to_install += @pods_by_state[:added] + @pods_by_state[:changed]
|
|
else
|
|
@pods_to_install = specs.map(&:name)
|
|
end
|
|
end
|
|
@pods_to_install
|
|
end
|
|
|
|
# @return [Array<Strings>] The name of the pods that were installed
|
|
# but don't have any dependency anymore. The name of the Pods are
|
|
# stripped from subspecs.
|
|
#
|
|
def removed_pods
|
|
return [] unless lockfile
|
|
unless @removed_pods
|
|
previously_installed = lockfile.pods_names.map { |pod_name| pod_name.split('/').first }
|
|
installed = specs.map { |spec| spec.name.split('/').first }
|
|
@removed_pods = previously_installed - installed
|
|
end
|
|
@removed_pods
|
|
end
|
|
|
|
private
|
|
|
|
# @return [Set] The cached set for a given dependency.
|
|
#
|
|
def find_cached_set(dependency, platform)
|
|
set_name = dependency.name.split('/').first
|
|
@cached_sets[set_name] ||= begin
|
|
if dependency.specification
|
|
Specification::Set::External.new(dependency.specification)
|
|
elsif external_source = dependency.external_source
|
|
if update_mode && update_external_specs
|
|
# Always update external sources in update mode.
|
|
specification = external_source.specification_from_external(@sandbox, platform)
|
|
else
|
|
# Don't update external sources in install mode if not needed.
|
|
specification = external_source.specification_from_sandbox(@sandbox, platform)
|
|
end
|
|
set = Specification::Set::External.new(specification)
|
|
if dependency.subspec_dependency?
|
|
@cached_sets[dependency.top_level_spec_name] ||= set
|
|
end
|
|
set
|
|
else
|
|
@cached_sources.search(dependency)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Resolves the dependencies of a specification and stores them in @cached_specs
|
|
#
|
|
# @param [Specification] dependent_specification
|
|
# @param [Array<Dependency>] dependencies
|
|
# @param [TargetDefinition] target_definition
|
|
#
|
|
# @return [void]
|
|
#
|
|
def find_dependency_specs(dependent_specification, dependencies, target_definition)
|
|
dependencies.each do |dependency|
|
|
# Replace the dependency with a more specific one if the pod is already installed.
|
|
if !update_mode && @pods_to_lock.include?(dependency.name)
|
|
dependency = lockfile.dependency_for_installed_pod_named(dependency.name)
|
|
end
|
|
UI.message("- #{dependency}", '', 2) do
|
|
set = find_cached_set(dependency, target_definition.platform)
|
|
set.required_by(dependency, dependent_specification.to_s)
|
|
|
|
# Ensure we don't resolve the same spec twice for one target
|
|
unless @loaded_specs.include?(dependency.name)
|
|
spec = set.specification_by_name(dependency.name)
|
|
@pods_from_external_sources << spec.pod_name if dependency.external?
|
|
@loaded_specs << spec.name
|
|
@cached_specs[spec.name] = spec
|
|
# Configure the specification
|
|
spec.activate_platform(target_definition.platform)
|
|
spec.version.head = dependency.head?
|
|
# And recursively load the dependencies of the spec.
|
|
find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
|
|
end
|
|
validate_platform(spec || @cached_specs[dependency.name], target_definition)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Ensures that a spec is compatible with the platform of a target.
|
|
#
|
|
# @raises If the spec is not supported by the target.
|
|
#
|
|
def validate_platform(spec, target)
|
|
unless spec.available_platforms.any? { |platform| target.platform.supports?(platform) }
|
|
raise Informative, "[!] The platform of the target `#{target.name}' (#{target.platform}) is not compatible with `#{spec}' which has a minimun requirement of #{spec.available_platforms.join(' - ')}.".red
|
|
end
|
|
end
|
|
end
|
|
end
|