Commit 9cbb2dfd authored by Fabio Pelosin's avatar Fabio Pelosin

[Install/Update] second iteration.

parent edea0a11
...@@ -36,16 +36,16 @@ module Pod ...@@ -36,16 +36,16 @@ module Pod
sandbox = Sandbox.new(config.project_pods_root) sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(podfile, lockfile, sandbox) resolver = Resolver.new(podfile, lockfile, sandbox)
resolver.update_mode = true resolver.update_mode = true
resolver.updated_external_specs = false resolver.update_external_specs = false
resolver.resolve resolver.resolve
specs_to_install = resolver.specs_to_install pods_to_install = resolver.pods_to_install
external_pods = resolver.external_pods external_pods = resolver.pods_from_external_sources
known_update_specs = [] known_update_specs = []
head_mode_specs = [] head_mode_specs = []
resolver.specs.each do |s| resolver.specs.each do |s|
next if external_pods.include?(s.name) next if external_pods.include?(s.name)
next unless specs_to_install.include?(s.name) next unless pods_to_install.include?(s.name)
if s.version.head? if s.version.head?
head_mode_specs << s.name head_mode_specs << s.name
...@@ -54,7 +54,7 @@ module Pod ...@@ -54,7 +54,7 @@ module Pod
end end
end end
if specs_to_install.empty? if pods_to_install.empty?
puts "\nNo updates are available.\n".yellow puts "\nNo updates are available.\n".yellow
else else
......
...@@ -101,6 +101,10 @@ module Pod ...@@ -101,6 +101,10 @@ module Pod
@external_source.specification_from_sandbox(sandbox, platform) @external_source.specification_from_sandbox(sandbox, platform)
end end
def match_version?(version)
match?(name, version) && (version.head? == head?)
end
# Taken from RubyGems 1.3.7 # Taken from RubyGems 1.3.7
unless public_method_defined?(:match?) unless public_method_defined?(:match?)
def match?(spec_name, spec_version) def match?(spec_name, spec_version)
...@@ -139,6 +143,7 @@ module Pod ...@@ -139,6 +143,7 @@ module Pod
module ExternalSources module ExternalSources
def self.from_params(name, params) def self.from_params(name, params)
return unless name && params
if params.key?(:git) if params.key?(:git)
GitSource.new(name, params) GitSource.new(name, params)
elsif params.key?(:podspec) elsif params.key?(:podspec)
...@@ -172,9 +177,9 @@ module Pod ...@@ -172,9 +177,9 @@ module Pod
specification_from_local(sandbox, platform) specification_from_local(sandbox, platform)
end end
def ==(other_source) def ==(other)
return if other_source.nil? return if other.nil?
name == other_source.name && params == other_source.params name == other.name && params == other.params
end end
end end
......
...@@ -79,6 +79,7 @@ module Pod ...@@ -79,6 +79,7 @@ module Pod
git! "reset --hard HEAD" git! "reset --hard HEAD"
git! "clean -d -x -f" git! "clean -d -x -f"
git! "pull origin master" git! "pull origin master"
git! "fetch --tags"
end end
end end
......
...@@ -58,11 +58,10 @@ module Pod ...@@ -58,11 +58,10 @@ module Pod
end end
if should_install if should_install
unless pod.downloaded?
pod.implode pod.implode
download_pod(pod) download_pod(pod)
# This will not happen if the pod existed before we started the install end
# process.
if pod.downloaded?
# The docs need to be generated before cleaning because the # The docs need to be generated before cleaning because the
# documentation is created for all the subspecs. # documentation is created for all the subspecs.
generate_docs(pod) generate_docs(pod)
...@@ -72,7 +71,6 @@ module Pod ...@@ -72,7 +71,6 @@ module Pod
end end
end end
end end
end
def download_pod(pod) def download_pod(pod)
downloader = Downloader.for_pod(pod) downloader = Downloader.for_pod(pod)
...@@ -100,12 +98,26 @@ module Pod ...@@ -100,12 +98,26 @@ module Pod
end end
end end
# @TODO: use the local pod implode
#
def remove_deleted_dependencies!
resolver.removed_pods.each do |pod_name|
marker = config.verbose ? "\n-> ".red : ''
path = sandbox.root + pod_name
puts marker << "Removing #{pod_name}".red
path.rmtree if path.exist?
end
end
def install! def install!
@sandbox.prepare_for_install @sandbox.prepare_for_install
print_title "Resolving dependencies of: #{@podfile.defined_in_file}" print_title "Resolving dependencies of: #{@podfile.defined_in_file}"
specs_by_target specs_by_target
print_title "Removing deleted dependencies" unless resolver.removed_pods.empty?
remove_deleted_dependencies!
print_title "Installing dependencies" print_title "Installing dependencies"
install_dependencies! install_dependencies!
...@@ -123,12 +135,12 @@ module Pod ...@@ -123,12 +135,12 @@ module Pod
# Post install hooks run _before_ saving of project, so that they can alter it before saving. # Post install hooks run _before_ saving of project, so that they can alter it before saving.
run_post_install_hooks run_post_install_hooks
puts "- Writing Xcode project file to `#{@sandbox.project_path}'\n\n" if config.verbose? puts "- Writing Xcode project file to `#{@sandbox.project_path}'" if config.verbose?
project.save_as(@sandbox.project_path) project.save_as(@sandbox.project_path)
puts "- Writing lockfile in `#{config.project_lockfile}'\n\n" if config.verbose? puts "- Writing lockfile in `#{config.project_lockfile}'\n\n" if config.verbose?
@lockfile = Lockfile.create(config.project_lockfile, @podfile, specs_by_target.values.flatten) @lockfile = Lockfile.generate(@podfile, specs_by_target.values.flatten)
@lockfile.write_to_disk @lockfile.write_to_disk(config.project_lockfile)
UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets? UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets?
end end
......
module Pod module Pod
class Lockfile class Lockfile
# @return [Lockfile] Returns the Lockfile saved in path. If the # @return [Lockfile] Returns the Lockfile saved in path.
# file could not be loaded or is not compatible with current # Returns {nil} If the file can't be loaded.
# version of CocoaPods {nil}
# #
def self.from_file(path) def self.from_file(path)
lockfile = Lockfile.new(path) return nil unless path.exist?
lockfile.hash_reppresentation ? lockfile : nil hash = YAML.load(File.open(path))
lockfile = Lockfile.new(hash)
lockfile.defined_in_file = path
lockfile
end end
# @return [Lockfile] Creates a new Lockfile ready to be saved in path. # @return [Lockfile] Generates a lockfile from a {Podfile} and the
# list of {Specifications} that were installed.
# #
def self.create(path, podfile, specs) def self.generate(podfile, specs)
Lockfile.new(path, podfile, specs) Lockfile.new(generate_hash_from_podfile(podfile, specs))
end end
attr_reader :defined_in_file, :podfile, :specs, :hash_reppresentation # @return [String] The file where this Lockfile is defined.
#
attr_accessor :defined_in_file
# @return [String] The hash used to initialize the Lockfile.
#
attr_reader :to_hash
# @param [Pathname] the path of the Lockfile. # @param [Pathname] the path of the Lockfile.
# If no other value is provided the Lockfile is read from this path. # If no other value is provided the Lockfile is read from this path.
# @param [Podfile] the Podfile to use for generating the Lockfile. # @param [Podfile] the Podfile to use for generating the Lockfile.
# @param [specs] the specs installed. # @param [specs] the specs installed.
# #
def initialize(path, podfile = nil, specs = nil) def initialize(hash)
@defined_in_file = path if Version.new(hash["COCOAPODS"]) <= Version.new("0.11")
if podfile && specs # Convert old format to be compatible
@podfile = podfile # - Pods:
@specs = specs # - libPusher (1.0) [HEAD] -> libPusher (HEAD from 1.0)
else # - Dependencies:
yaml = YAML.load(File.open(path)) # - libPusher [HEAD] -> libPusher (HEAD)
if yaml && Version.new(yaml["COCOAPODS"]) >= Version.new("0.10")
@hash_reppresentation = yaml
end
end end
@to_hash = hash
end end
# @return [Array<String, Hash{String => Array[String]}>] The pods installed
# and their dependencies.
#
def pods def pods
return [] unless to_hash @pods ||= to_hash['PODS'] || []
to_hash['PODS'] || []
end end
# @return [Array<Dependency>] The Podfile dependencies used during the last
# install.
#
def dependencies def dependencies
return [] unless to_hash @dependencies ||= to_hash['DEPENDENCIES'].map { |dep| dependency_from_string(dep) } || []
to_hash['DEPENDENCIES'] || []
end end
# @return [Hash{String => Hash}] A hash where the name of the pods are
# the keys and the values are the parameters of an {AbstractExternalSource}
# of the dependency that required the pod.
#
def external_sources def external_sources
return [] unless to_hash @external_sources ||= to_hash["EXTERNAL SOURCES"] || {}
to_hash["EXTERNAL SOURCES"] || []
end end
# @return [Array<Dependency>] The Podfile dependencies used during the last # @return [Array<String>] The names of the installed Pods.
# install.
# #
def podfile_dependencies def pods_names
dependencies.map { |dep| dependency_from_string(dep) } @pods_names ||= pods.map do |pod|
pod = pod.keys.first unless pod.is_a?(String)
name_and_version_for_pod(pod)[0]
end
end end
# @return [Array<Dependency>] The dependencies that require the installed # @return [Hash{String => Version}] A Hash containing the name
# pods with their exact version. # of the installed Pods as the keys and their corresponding {Version}
# as the values.
# #
def dependencies_for_pods def pods_versions
pods.map { |pod| dependency_from_string(pod.is_a?(String) ? pod : pod.keys[0]) } unless @pods_versions
@pods_versions = {}
pods.each do |pod|
pod = pod.keys.first unless pod.is_a?(String)
name, version = name_and_version_for_pod(pod)
@pods_versions[name] = version
end
end
@pods_versions
end end
# @param [String] The string that describes a {Specification} generated
# from {Specification#to_s}.
#
# @example Strings examples
# "libPusher"
# "libPusher (1.0)"
# "libPusher (HEAD from 1.0)"
# "RestKit/JSON"
#
# @return [String, Version] The name and the version of a
# pod.
#
def name_and_version_for_pod(string)
match_data = string.match(/(\S*) \((.*)\)/)
name = match_data[1]
vers = Version.from_s(match_data[2])
return [name, vers]
end
# @param [String] The string that describes a {Dependency} generated
# from {Dependency#to_s}.
#
# @example Strings examples
# "libPusher"
# "libPusher (= 1.0)"
# "libPusher (~> 1.0.1)"
# "libPusher (> 1.0, < 2.0)"
# "libPusher (HEAD)"
# "libPusher (from `www.example.com')"
# "libPusher (defined in Podfile)"
# "RestKit/JSON"
#
# @return [Dependency] The dependency described by the string. # @return [Dependency] The dependency described by the string.
# #
def dependency_from_string(string) def dependency_from_string(string)
...@@ -79,8 +136,8 @@ module Pod ...@@ -79,8 +136,8 @@ module Pod
# @TODO: store the whole spec?, the version? # @TODO: store the whole spec?, the version?
Dependency.new(name) Dependency.new(name)
when /from `(.*)'/ when /from `(.*)'/
external_source_info = external_sources.find {|hash| hash.keys[0] == name} || {} external_source_info = external_sources[name]
Dependency.new(name, external_source_info[name]) Dependency.new(name, external_source_info)
when /HEAD/ when /HEAD/
# @TODO: find a way to serialize from the Downloader the information # @TODO: find a way to serialize from the Downloader the information
# necessary to restore a head version. # necessary to restore a head version.
...@@ -90,10 +147,60 @@ module Pod ...@@ -90,10 +147,60 @@ module Pod
end end
end end
# Analyzes the {Lockfile} and detects any changes applied to the {Podfile}
# since the last installation.
#
# For each Pod, it detects one state among the following:
#
# - added: Pods that weren't present in the Podfile.
# - changed: Pods that were present in the Podfile but changed:
# - Pods whose version is not compatible anymore with Podfile,
# - Pods that changed their head or external options.
# - removed: Pods that were removed form the Podfile.
# - unchanged: Pods that are still compatible with Podfile.
#
# @TODO: detect changes for inline dependencies?
#
# @return [Hash{Symbol=>Array[Strings]}] A hash where pods are grouped
# by the state in which they are.
#
def detect_changes_with_podfile(podfile)
previous_podfile_deps = dependencies.map(&:name)
user_installed_pods = pods_names.reject { |name| !previous_podfile_deps.include?(name) }
deps_to_install = podfile.dependencies.dup
result = {}
result[:added] = []
result[:changed] = []
result[:removed] = []
result[:unchanged] = []
user_installed_pods.each do |pod_name|
dependency = deps_to_install.find { |d| d.name == pod_name }
deps_to_install.delete(dependency)
version = pods_versions[pod_name]
external_source = Dependency::ExternalSources.from_params(pod_name, external_sources[pod_name])
if dependency.nil?
result[:removed] << pod_name
elsif !dependency.match_version?(version) || dependency.external_source != external_source
result[:changed] << pod_name
else
result[:unchanged] << pod_name
end
end
deps_to_install.each do |dependency|
result[:added] << dependency.name
end
result
end
# @return [void] Writes the Lockfile to {#path}. # @return [void] Writes the Lockfile to {#path}.
# #
def write_to_disk def write_to_disk(path)
File.open(defined_in_file, 'w') {|f| f.write(to_yaml) } File.open(path, 'w') {|f| f.write(to_yaml) }
defined_in_file = path
end end
# @return [String] A string useful to represent the Lockfile in a message # @return [String] A string useful to represent the Lockfile in a message
...@@ -112,9 +219,7 @@ module Pod ...@@ -112,9 +219,7 @@ module Pod
# @return [Dictionary] The Dictionary representation of the Lockfile. # @return [Dictionary] The Dictionary representation of the Lockfile.
# #
def to_hash def self.generate_hash_from_podfile(podfile, specs)
return @hash_reppresentation if @hash_reppresentation
return nil unless @podfile && @specs
hash = {} hash = {}
# Get list of [name, dependencies] pairs. # Get list of [name, dependencies] pairs.
...@@ -122,7 +227,7 @@ module Pod ...@@ -122,7 +227,7 @@ module Pod
[spec.to_s, spec.dependencies.map(&:to_s).sort] [spec.to_s, spec.dependencies.map(&:to_s).sort]
end.uniq end.uniq
# Merge dependencies of ios and osx version of the same pod. # Merge dependencies of iOS and OS X version of the same pod.
tmp = {} tmp = {}
pod_and_deps.each do |name, deps| pod_and_deps.each do |name, deps|
if tmp[name] if tmp[name]
...@@ -136,12 +241,18 @@ module Pod ...@@ -136,12 +241,18 @@ module Pod
end end
hash["PODS"] = pod_and_deps hash["PODS"] = pod_and_deps
hash["DEPENDENCIES"] = podfile.dependencies.map{ |d| "#{d}" }.sort hash["DEPENDENCIES"] = podfile.dependencies.map{ |d| d.to_s }.sort
external_sources = podfile.dependencies.select(&:external?).sort{ |d, other| d.name <=> other.name}.map{ |d| { d.name => d.external_source.params } } external_sources = {}
deps = podfile.dependencies.select(&:external?).sort{ |d, other| d.name <=> other.name}
deps.each{ |d| external_sources[d.name] = d.external_source.params }
hash["EXTERNAL SOURCES"] = external_sources unless external_sources.empty? hash["EXTERNAL SOURCES"] = external_sources unless external_sources.empty?
# hash["SPECS_CHECKSUM"] checksums = {}
specs.select {|spec| !spec.defined_in_file.nil? }.each do |spec|
checksums[spec.name] = Digest::SHA1.hexdigest(File.read(spec.defined_in_file)).to_s
end
hash["SPECS CHECKSUM"] = checksums unless checksums.empty?
hash["COCOAPODS"] = VERSION hash["COCOAPODS"] = VERSION
hash hash
end end
......
...@@ -4,6 +4,16 @@ module Pod ...@@ -4,6 +4,16 @@ module Pod
class Resolver class Resolver
include Config::Mixin 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. # @return [Podfile] The Podfile used by the resolver.
# #
attr_reader :podfile attr_reader :podfile
...@@ -17,142 +27,127 @@ module Pod ...@@ -17,142 +27,127 @@ module Pod
# #
attr_reader :sandbox attr_reader :sandbox
# @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 :updated_external_specs
# @return [Array<Strings>] The name of the pods coming from an # @return [Array<Strings>] The name of the pods coming from an
# external sources # external sources
# #
attr_reader :external_pods attr_reader :pods_from_external_sources
# @return [Array<Set>] The set used to resolve the dependencies. # @return [Array<Set>] The set used to resolve the dependencies.
# #
attr_accessor :cached_sets attr_reader :cached_sets
# @return [Source::Aggregate] A cache of the sources needed to find the # @return [Source::Aggregate] A cache of the sources needed to find the
# podspecs. # podspecs.
# #
attr_accessor :cached_sources 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) def initialize(podfile, lockfile, sandbox)
@podfile = podfile @podfile = podfile
@lockfile = lockfile @lockfile = lockfile
@sandbox = sandbox @sandbox = sandbox
@update_external_specs = true
@cached_sets = {} @cached_sets = {}
@cached_sources = Source::Aggregate.new @cached_sources = Source::Aggregate.new
@cached_specs = {}
@specs_by_target = {}
@pods_from_external_sources = []
@dependencies_podfile_incompatible = []
@log_indent = 0; @log_indent = 0;
@updated_external_specs = true
end end
# Identifies the specifications that should be installed according whether # Identifies the specifications that should be installed according whether
# the resolver is in update mode or not. # the resolver is in update mode or not.
# #
# @return [void] # @return [Hash{Podfile::TargetDefinition => Array<Specification>}] specs_by_target
# #
def resolve def resolve
if @lockfile
puts "\nFinding added, modified or removed dependencies:".green if config.verbose?
@pods_by_state = @lockfile.detect_changes_with_podfile(podfile)
if config.verbose? if config.verbose?
unless podfile_dependencies.empty? @pods_by_state.each do |symbol, pod_names|
puts "\nAlready installed Podfile dependencies detected (Podfile.lock):".green case symbol
podfile_dependencies.each {|dependency| puts " - #{dependency}" } when :added
mark = "A".green
when :changed
mark = "M".yellow
when :removed
mark = "R".red
when :unchanged
mark = "-"
end end
unless dependencies_for_pods.empty? pod_names.each do |pod_name|
puts "\nInstalled Pods detected (Podfile.lock):".green puts " #{mark} " << pod_name
dependencies_for_pods.each {|dependency| puts " - #{dependency}" }
end end
end end
end
@cached_specs = {} pods_not_to_lock = @pods_by_state[:added] + @pods_by_state[:changed] + @pods_by_state[:removed]
@targets_and_specs = {} lock_versions(lockfile.pods_names - pods_not_to_lock) unless update_mode
@external_pods = [] end
@dependencies_podfile_incompatible = []
@removed_pods = []
lock_dependencies_version unless update_mode
@podfile.target_definitions.values.each do |target_definition| @podfile.target_definitions.values.each do |target_definition|
puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform}):".green if config.verbose? puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform}):".green if config.verbose?
@loaded_specs = [] @loaded_specs = []
find_dependency_specs(@podfile, target_definition.dependencies, target_definition) find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
@targets_and_specs[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name) @specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
end end
@cached_specs.values.sort_by(&:name) @cached_specs.values.sort_by(&:name)
@targets_and_specs @specs_by_target
end
# @return [Array<Specification>] The specifications loaded by the resolver.
#
def specs
@cached_specs.values.uniq
end end
# @return [Bool] Whether a pod should be installed/reinstalled. # @return [Bool] Whether a pod should be installed/reinstalled.
# #
def should_install?(name) def should_install?(name)
specs_to_install.include?(name) pods_to_install.include? name
end end
# @return [Array<String>] The list of the names of the pods that need # @return [Array<Strings>] The name of the pods that should be
# to be installed. # installed/reinstalled.
#
# - Install mode: a specification will be installed only if its
# dependency in Podfile changed since the last installation.
# New Pods will always be installed and Pods already installed will be
# reinstalled only if they are not compatible anymore with the Podfile.
# - Update mode: a Pod will be installed only if there is a new
# version and it was already installed. In no case new Pods will be
# installed.
# #
def specs_to_install def pods_to_install
@specs_to_install ||= begin unless @pods_to_install
specs = @targets_and_specs.values.flatten if lockfile
to_install = [] @pods_to_install = specs.select { |spec|
specs.each do |spec| spec.version != lockfile.pods_versions[spec.pod_name]
}.map(&:name)
if update_mode if update_mode
# Installation mode @pods_to_install += specs.select { |spec|
installed_dependency = dependencies_for_pods.find{|d| d.name == spec.name } spec.version.head? || pods_from_external_sources.include?(spec.pod_name)
outdated = installed_dependency && !installed_dependency.matches_spec?(spec) }.map(&:name)
head = spec.version.head?
if outdated || head || @external_pods.include?(spec.pod_name)
to_install << spec
end end
@pods_to_install += @pods_by_state[:added] + @pods_by_state[:changed]
else else
# Installation mode @pods_to_install = specs.map(&:name)
spec_incompatible_with_podfile = @dependencies_podfile_incompatible.any?{ |d| d.name == spec.name }
spec_installed = dependencies_for_pods.any?{ |d| d.name == spec.name }
if !spec_installed || spec_incompatible_with_podfile
to_install << spec unless @external_pods.include?(spec.pod_name)
end
end
end end
to_install.map{ |s| s.top_level_parent.name }.uniq
end
end
# @return [Array<Specification>] The specifications loaded by the resolver.
#
def specs
@cached_specs.values.uniq
end end
@pods_to_install
# @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
# Returns the resolved specifications grouped by target.
#
def specs_by_target
@targets_and_specs
end end
# @return [Array<Strings>] The name of the pods that were installed # @return [Array<Strings>] The name of the pods that were installed
# but don't have any dependency anymore. # but don't have any dependency anymore. It returns the name
# of the Pod stripped from subspecs.
# #
def removed_pods def removed_pods
if update_mode return [] unless lockfile
[] # It should never remove any pod in update mode unless @removed_pods
else previusly_installed = lockfile.pods_names.map { |pod_name| pod_name.split('/').first }
[] # @TODO: Implement installed = specs.map { |spec| spec.name.split('/').first }
@removed_pods = previusly_installed - installed
end end
@removed_pods
end end
private private
...@@ -162,53 +157,18 @@ module Pod ...@@ -162,53 +157,18 @@ module Pod
# #
# @return [void] # @return [void]
# #
def lock_dependencies_version def lock_versions(pods)
return unless lockfile return unless lockfile
# Add a specific Dependency to lock the version in the resolution process
puts "\nFinding updated or removed pods:".green if config.verbose? pods.each do |pod_name|
podfile_deps_names = podfile_dependencies.map(&:name) version = lockfile.pods_versions[pod_name]
raise Informative, "Attempt to lock a Pod without an known version." unless version
dependencies_for_pods.each do |dependency| dependency = Dependency.new(pod_name, version)
# Skip the dependency if it was not requested in the Podfile in the
# previous installation.
next unless podfile_deps_names.include?(dependency.name)
podfile_dependency = podfile.dependencies.find { |d| d.name == dependency.name }
# Don't lock the dependency if it can't be found in the Podfile as it
# it means that it was removed.
unless podfile_dependency
puts " R ".red << dependency.to_s if config.verbose?
@removed_pods << dependency.name #TODO: use the pod name?
next
end
# Check if the dependency necessary to load the pod is still compatible with
# the podfile.
# @TODO: pattern match might not be the most appropriate method.
if podfile_dependency =~ dependency
puts " - " << dependency.to_s if config.verbose?
set = find_cached_set(dependency, nil) set = find_cached_set(dependency, nil)
set.required_by(dependency, lockfile.to_s) set.required_by(dependency, lockfile.to_s)
else
puts " U ".yellow << "#{dependency} -> #{podfile_dependency}" if config.verbose?
@dependencies_podfile_incompatible << dependency
end
end
end
# @return [Array<Dependency>] Cached copy of the dependencies that require
# the installed pods with their exact version.
#
def dependencies_for_pods
@dependencies_for_pods ||= lockfile ? lockfile.dependencies_for_pods : []
end end
# @return [Array<Dependency>] Cached copy of the Podfile dependencies used
# during the last install.
#
def podfile_dependencies
@podfile_dependencies ||= lockfile ? lockfile.podfile_dependencies : []
end end
# @return [Set] The cached set for a given dependency. # @return [Set] The cached set for a given dependency.
# #
def find_cached_set(dependency, platform) def find_cached_set(dependency, platform)
...@@ -217,12 +177,11 @@ module Pod ...@@ -217,12 +177,11 @@ module Pod
if dependency.specification if dependency.specification
Specification::Set::External.new(dependency.specification) Specification::Set::External.new(dependency.specification)
elsif external_source = dependency.external_source elsif external_source = dependency.external_source
# The platform isn't actually being used by the LocalPod instance if update_mode && update_external_specs
# that's being used behind the scenes, but passing it anyways for # Always update external sources in update mode.
# completeness sake.
if update_mode && updated_external_specs
specification = external_source.specification_from_external(@sandbox, platform) specification = external_source.specification_from_external(@sandbox, platform)
else else
# Don't update external sources in install mode if not needed.
specification = external_source.specification_from_sandbox(@sandbox, platform) specification = external_source.specification_from_sandbox(@sandbox, platform)
end end
set = Specification::Set::External.new(specification) set = Specification::Set::External.new(specification)
...@@ -238,6 +197,10 @@ module Pod ...@@ -238,6 +197,10 @@ module Pod
# Resolves the dependencies of a specification and stores them in @cached_specs # 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] # @return [void]
# #
def find_dependency_specs(dependent_specification, dependencies, target_definition) def find_dependency_specs(dependent_specification, dependencies, target_definition)
...@@ -247,18 +210,10 @@ module Pod ...@@ -247,18 +210,10 @@ module Pod
set = find_cached_set(dependency, target_definition.platform) set = find_cached_set(dependency, target_definition.platform)
set.required_by(dependency, dependent_specification.to_s) set.required_by(dependency, dependent_specification.to_s)
# Ensure that we don't load new pods in update mode
# @TODO: filter the dependencies of the target before calling #find_dependency_specs
if update_mode
mode_wants_spec = dependencies_for_pods.any?{ |d| d.name == dependency.name }
else
mode_wants_spec = true
end
# Ensure we don't resolve the same spec twice for one target # Ensure we don't resolve the same spec twice for one target
if mode_wants_spec && !@loaded_specs.include?(dependency.name) unless @loaded_specs.include?(dependency.name)
spec = set.specification_by_name(dependency.name) spec = set.specification_by_name(dependency.name)
@external_pods << spec.pod_name if dependency.external? @pods_from_external_sources << spec.pod_name if dependency.external?
@loaded_specs << spec.name @loaded_specs << spec.name
@cached_specs[spec.name] = spec @cached_specs[spec.name] = spec
# Configure the specification # Configure the specification
......
...@@ -73,14 +73,10 @@ else ...@@ -73,14 +73,10 @@ else
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root)) resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = Pod::Installer.new(resolver) installer = Pod::Installer.new(resolver)
installer.install! installer.install!
installer.lockfile.to_hash.should == { result = installer.lockfile.to_hash
'PODS' => ['SSToolkit (0.1.3)'], result['PODS'].should == ['SSToolkit (0.1.3)']
'DEPENDENCIES' => ["SSToolkit (from `#{url}', commit `#{commit}')"], result['DEPENDENCIES'].should == ["SSToolkit (from `#{url}', commit `#{commit}')"]
'EXTERNAL SOURCES' => [ {"SSToolkit" => { result['EXTERNAL SOURCES'].should == {"SSToolkit" => { :git=>url, :commit=>commit}}
:git=>"/Users/fabio/Documents/GitHub/CP/CocoaPods/spec/fixtures/integration/sstoolkit", :commit=>"2adcd0f81740d6b0cd4589af98790eee3bd1ae7b"
}}],
'COCOAPODS' => Pod::VERSION
}
end end
it "installs a library with a podspec outside of the repo" do it "installs a library with a podspec outside of the repo" do
...@@ -92,18 +88,13 @@ else ...@@ -92,18 +88,13 @@ else
pod 'Reachability', :podspec => url pod 'Reachability', :podspec => url
end end
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root)) resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver) installer = SpecHelper::Installer.new(resolver)
installer.install! installer.install!
result = installer.lockfile.to_hash
installer.lockfile.to_hash.should == { result['PODS'].should == ['Reachability (1.2.3)']
'PODS' => [ 'Reachability (1.2.3)' ], result['DEPENDENCIES'].should == ["Reachability (from `#{url}')"]
'DEPENDENCIES' => ["Reachability (from `#{url}')"], result['EXTERNAL SOURCES'].should == {"Reachability"=>{ :podspec=>"https://raw.github.com/gist/1349824/3ec6aa60c19113573fc48eac19d0fafd6a69e033/Reachability.podspec"}}
"EXTERNAL SOURCES"=>[{"Reachability"=>{
:podspec=>"https://raw.github.com/gist/1349824/3ec6aa60c19113573fc48eac19d0fafd6a69e033/Reachability.podspec"}}],
'COCOAPODS' => Pod::VERSION
}
end end
it "installs a dummy source file" do it "installs a dummy source file" do
...@@ -203,19 +194,19 @@ else ...@@ -203,19 +194,19 @@ else
installer = SpecHelper::Installer.new(resolver) installer = SpecHelper::Installer.new(resolver)
installer.install! installer.install!
# TODO might be nicer looking to not show the dependencies of the top level spec for each subspec (Reachability). result = installer.lockfile.to_hash
installer.lockfile.to_hash.should == { result['PODS'].should == [
"PODS" => [{ "ASIHTTPRequest (1.8.1)" => ["ASIHTTPRequest/ASIWebPageRequest (= 1.8.1)", { "ASIHTTPRequest (1.8.1)" => ["ASIHTTPRequest/ASIWebPageRequest (= 1.8.1)",
"ASIHTTPRequest/CloudFiles (= 1.8.1)", "ASIHTTPRequest/CloudFiles (= 1.8.1)",
"ASIHTTPRequest/S3 (= 1.8.1)", "ASIHTTPRequest/S3 (= 1.8.1)",
"Reachability"]}, "Reachability"]},
{ "ASIHTTPRequest/ASIWebPageRequest (1.8.1)" => ["Reachability"] }, { "ASIHTTPRequest/ASIWebPageRequest (1.8.1)" => ["Reachability"] },
{ "ASIHTTPRequest/CloudFiles (1.8.1)" => ["Reachability"] }, { "ASIHTTPRequest/CloudFiles (1.8.1)" => ["Reachability"] },
{ "ASIHTTPRequest/S3 (1.8.1)" => ["Reachability"] }, { "ASIHTTPRequest/S3 (1.8.1)" => ["Reachability"] },
"JSONKit (1.4)", "Reachability (3.0.0)"], "JSONKit (1.4)",
"DEPENDENCIES" => ["ASIHTTPRequest", "JSONKit (= 1.4)"], "Reachability (3.0.0)"]
"COCOAPODS" => Pod::VERSION result['DEPENDENCIES'].should == ["ASIHTTPRequest", "JSONKit (= 1.4)"]
} # TODO might be nicer looking to not show the dependencies of the top level spec for each subspec (Reachability).
should_xcodebuild(podfile.target_definitions[:ios_target]) should_xcodebuild(podfile.target_definitions[:ios_target])
should_xcodebuild(podfile.target_definitions[:osx_target]) should_xcodebuild(podfile.target_definitions[:osx_target])
...@@ -313,7 +304,9 @@ else ...@@ -313,7 +304,9 @@ else
lockfile_contents['PODS'].delete_at(1) lockfile_contents['PODS'].delete_at(1)
# lockfile_contents['PODS'][0] = 'ASIHTTPRequest (1.8.1)' # lockfile_contents['PODS'][0] = 'ASIHTTPRequest (1.8.1)'
end end
installer.lockfile.to_hash.should == lockfile_contents result = installer.lockfile.to_hash
result.delete("SPECS CHECKSUM")
result.should == lockfile_contents
root = config.project_pods_root root = config.project_pods_root
(root + 'Pods.xcconfig').read.should == installer.target_installers.first.xcconfig.to_s (root + 'Pods.xcconfig').read.should == installer.target_installers.first.xcconfig.to_s
...@@ -335,12 +328,9 @@ else ...@@ -335,12 +328,9 @@ else
installer = SpecHelper::Installer.new(resolver) installer = SpecHelper::Installer.new(resolver)
installer.install! installer.install!
installer.lockfile.to_hash.tap {|d| d.delete("COCOAPODS") }.should == { result = installer.lockfile.to_hash
# 'PODS' => [{ 'Reachability (2.0.4)' => ["ASIHTTPRequest (>= 1.8)"] }], result['PODS'].should == [ 'Reachability (2.0.4)' ]
'PODS' => [ 'Reachability (2.0.4)' ], result['DEPENDENCIES'].should == ["Reachability (= 2.0.4)"]
# 'DOWNLOAD_ONLY' => ["ASIHTTPRequest (1.8.1)"],
'DEPENDENCIES' => ["Reachability (= 2.0.4)"]
}
end end
end end
......
require File.expand_path('../../spec_helper', __FILE__) require File.expand_path('../../spec_helper', __FILE__)
describe "Pod::Lockfile" do describe "Pod::Lockfile" do
describe "In general" do
extend SpecHelper::TemporaryDirectory extend SpecHelper::TemporaryDirectory
def sample def sample
text = <<-LOCKFILE.strip_heredoc text = <<-LOCKFILE.strip_heredoc
PODS: PODS:
- BananaLib (1.0): - BananaLib (1.0):
- monkey (< 1.0.9, ~> 1.0.1) - monkey (< 1.0.9, ~> 1.0.1)
- monkey (1.0.8) - monkey (1.0.8)
DEPENDENCIES:
- BananaLib (~> 1.0) DEPENDENCIES:
COCOAPODS: #{Pod::VERSION} - BananaLib (~> 1.0)
SPECS CHECKSUM:
BananaLib: !binary |-
MjI2Y2RkMTJkMzBhMWU4ZWM4OGM1ZmRkZWU2MDcwZDg0YTI1MGZjMQ==
COCOAPODS: 0.11.1
LOCKFILE LOCKFILE
end end
...@@ -38,6 +46,11 @@ describe "Pod::Lockfile" do ...@@ -38,6 +46,11 @@ describe "Pod::Lockfile" do
temporary_directory + 'Podfile.lock' temporary_directory + 'Podfile.lock'
end end
it "loads from a hash" do
lockfile = Pod::Lockfile.new(YAML.load(sample))
lockfile.to_hash.should == YAML.load(sample)
end
it "loads from a file" do it "loads from a file" do
File.open(tmp_path, 'w') {|f| f.write(sample) } File.open(tmp_path, 'w') {|f| f.write(sample) }
lockfile = Pod::Lockfile.from_file(tmp_path) lockfile = Pod::Lockfile.from_file(tmp_path)
...@@ -45,8 +58,13 @@ describe "Pod::Lockfile" do ...@@ -45,8 +58,13 @@ describe "Pod::Lockfile" do
lockfile.to_hash.should == YAML.load(sample) lockfile.to_hash.should == YAML.load(sample)
end end
it "can be generated from a Podfile and a list of Specifications" do
lockfile = Pod::Lockfile.generate(podfile, specs)
lockfile.to_hash.should == YAML.load(sample)
end
before do before do
@lockfile = Pod::Lockfile.create(tmp_path, podfile, specs) @lockfile = Pod::Lockfile.generate(podfile, specs)
end end
it "generates a valid YAML representation" do it "generates a valid YAML representation" do
...@@ -57,23 +75,15 @@ describe "Pod::Lockfile" do ...@@ -57,23 +75,15 @@ describe "Pod::Lockfile" do
@lockfile.to_hash.should == YAML.load(sample) @lockfile.to_hash.should == YAML.load(sample)
end end
it "returns the Podfile dependencies" do it "returns the list of the installed pods" do
@lockfile.podfile_dependencies.should == [ @lockfile.pods_names.should == %w| BananaLib monkey |
Pod::Dependency.new("BananaLib", "~> 1.0")
]
end end
it "returns the dependencies for the installed pods" do it "returns the versions of the installed pods" do
@lockfile.dependencies_for_pods.should == [ @lockfile.pods_versions.should == {
Pod::Dependency.new("BananaLib", "= 1.0"), "BananaLib" => Pod::Version.new("1.0"),
Pod::Dependency.new("monkey", "= 1.0.8") "monkey" => Pod::Version.new("1.0.8")
] }
end
it "can check if it is compatible with a file" do
File.open(tmp_path, 'w') {|f| f.write(sample.gsub("COCOAPODS: #{Pod::VERSION}", "")) }
lockfile = Pod::Lockfile.from_file(tmp_path)
lockfile.should == nil
end end
it "serializes correctly `:head' dependencies" do it "serializes correctly `:head' dependencies" do
...@@ -92,7 +102,7 @@ describe "Pod::Lockfile" do ...@@ -92,7 +102,7 @@ describe "Pod::Lockfile" do
end end
] ]
specs.each { |s| s.activate_platform(:ios) } specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.create(tmp_path, podfile, specs) lockfile = Pod::Lockfile.generate(podfile, specs)
lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (HEAD)" lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (HEAD)"
end end
...@@ -112,9 +122,9 @@ describe "Pod::Lockfile" do ...@@ -112,9 +122,9 @@ describe "Pod::Lockfile" do
end end
] ]
specs.each { |s| s.activate_platform(:ios) } specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.create(tmp_path, podfile, specs) lockfile = Pod::Lockfile.generate(podfile, specs)
lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (from `www.example.com', tag `1.0')" lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (from `www.example.com', tag `1.0')"
lockfile.to_hash["EXTERNAL SOURCES"][0]["BananaLib"].should == { :git => "www.example.com", :tag => '1.0' } lockfile.to_hash["EXTERNAL SOURCES"]["BananaLib"].should == { :git => "www.example.com", :tag => '1.0' }
end end
it "creates a dependency from a string" do it "creates a dependency from a string" do
...@@ -138,11 +148,184 @@ describe "Pod::Lockfile" do ...@@ -138,11 +148,184 @@ describe "Pod::Lockfile" do
platform :ios platform :ios
pod 'BananaLib', { :git => "www.example.com", :tag => '1.0' } pod 'BananaLib', { :git => "www.example.com", :tag => '1.0' }
end end
lockfile = Pod::Lockfile.create(tmp_path, podfile, []) lockfile = Pod::Lockfile.generate(podfile, [])
d = lockfile.dependency_from_string("BananaLib (from `www.example.com', tag `1.0')") d = lockfile.dependency_from_string("BananaLib (from `www.example.com', tag `1.0')")
d.name.should == "BananaLib" d.name.should == "BananaLib"
d.requirement.should.be.none? d.requirement.should.be.none?
d.external?.should.be.true d.external?.should.be.true
d.external_source.description.should == "from `www.example.com', tag `1.0'" d.external_source.description.should == "from `www.example.com', tag `1.0'"
end end
end
describe "Concerning initialization from a file" do
extend SpecHelper::TemporaryDirectory
it "returns nil if it can't find the initialization file" do
lockfile = Pod::Lockfile.from_file(temporary_directory + 'Podfile.lock_not_existing')
lockfile.should == nil
end
xit "updates files generated by old versions of CocoaPods" do
tmp_path = temporary_directory + 'Podfile.lock'
text = <<-LOCKFILE.strip_heredoc
PODS:
- BananaLib (1.0) [HEAD]:
- monkey (< 1.0.9, ~> 1.0.1)
- monkey (1.0.8)
DEPENDENCIES:
- BananaLib (HEAD)
LOCKFILE
File.open(tmp_path, 'w') {|f| f.write(text) }
lockfile = Pod::Lockfile.from_file(tmp_path)
lockfile.to_hash.should == {
"PODS"=>[
{"BananaLib (HEAD from 1.0)" => ["monkey (< 1.0.9, ~> 1.0.1)"]},
"monkey (1.0.8)"
],
"DEPENDENCIES" => ["BananaLib (~> 1.0)"]
}
end
end
describe "Concerning the identification of changes in the Podfile" do
before do
@podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
end
@specs = [
Pod::Specification.new do |s|
s.name = "BlocksKit"
s.version = "1.0.0"
end,
Pod::Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Pod::Lockfile.generate(@podfile, @specs)
end
it "detects an added Pod" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
pod 'TTTAttributedLabel'
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>[],
:removed=>[],
:unchanged=>["BlocksKit", "JSONKit"],
:added=>["TTTAttributedLabel"]
}
end
it "detects an removed Pod" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>[],
:removed=>["JSONKit"],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
it "detects Pods whose version changed" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', "> 1.4"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
it "it doesn't mark a changed Pods whose version changed but is still compatible with the Podfile" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', "> 1.0"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>[],
:removed=>[],
:unchanged=>["BlocksKit", "JSONKit"],
:added=>[]
}
end
it "detects Pods whose external source changed" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :git => "example1.com"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
@lockfile = Pod::Lockfile.generate(podfile, @specs)
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :git => "example2.com"
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
it "detects Pods whose head state changed" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', :head
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
@specs = [
Pod::Specification.new do |s|
s.name = "BlocksKit"
s.version = "1.0.0"
end,
Pod::Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
s.version.head = true
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Pod::Lockfile.generate(podfile, @specs)
podfile = Pod::Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
end
@lockfile.detect_changes_with_podfile(podfile).should == {
:changed=>["JSONKit"],
:removed=>[],
:unchanged=>["BlocksKit"],
:added=>[]
}
end
end
end end
...@@ -223,7 +223,7 @@ module Pod ...@@ -223,7 +223,7 @@ module Pod
s.version = "1.4" s.version = "1.4"
end ] end ]
@specs.each { |s| s.activate_platform(:ios) } @specs.each { |s| s.activate_platform(:ios) }
@lockfile = Lockfile.create(nil, @podfile, @specs) @lockfile = Lockfile.generate(@podfile, @specs)
@resolver = Resolver.new(@podfile, @lockfile, stub('sandbox')) @resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
end end
...@@ -233,7 +233,7 @@ module Pod ...@@ -233,7 +233,7 @@ module Pod
@resolver.should_install?("JSONKit").should.be.false @resolver.should_install?("JSONKit").should.be.false
end end
it "doesn't updates pods still compatible with the Podfile" do it "doesn't update the version of pods still compatible with the Podfile" do
installed = @resolver.resolve.values.flatten.map(&:to_s) installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (1.4)" installed.should.include? "JSONKit (1.4)"
end end
...@@ -315,7 +315,7 @@ module Pod ...@@ -315,7 +315,7 @@ module Pod
s.version = "1.4" s.version = "1.4"
end ] end ]
@specs.each { |s| s.activate_platform(:ios) } @specs.each { |s| s.activate_platform(:ios) }
@lockfile = Lockfile.create(nil, @podfile, @specs) @lockfile = Lockfile.generate(@podfile, @specs)
@resolver = Resolver.new(@podfile, @lockfile, stub('sandbox')) @resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true @resolver.update_mode = true
end end
...@@ -326,7 +326,7 @@ module Pod ...@@ -326,7 +326,7 @@ module Pod
@resolver.should_install?("JSONKit").should.be.true @resolver.should_install?("JSONKit").should.be.true
end end
it "respecs the constraints of the pofile" do it "respects the constraints of the pofile" do
podfile = Podfile.new do podfile = Podfile.new do
platform :ios platform :ios
pod 'BlocksKit' pod 'BlocksKit'
...@@ -339,10 +339,10 @@ module Pod ...@@ -339,10 +339,10 @@ module Pod
@resolver.should_install?("JSONKit").should.be.false @resolver.should_install?("JSONKit").should.be.false
end end
it "doesn't install new pods in `update_mode'" do it "installs new pods" do
installed = @resolver.resolve.values.flatten.map(&:to_s) installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.join(' ').should.not.include?('BlocksKit') installed.join(' ').should.include?('BlocksKit')
@resolver.should_install?("BlocksKit").should.be.false @resolver.should_install?("BlocksKit").should.be.true
end end
it "it always suggests to update pods in head mode" do it "it always suggests to update pods in head mode" do
...@@ -356,8 +356,15 @@ module Pod ...@@ -356,8 +356,15 @@ module Pod
@resolver.should_install?("libPusher").should.be.true @resolver.should_install?("libPusher").should.be.true
end end
xit "it suggests to update pods from external sources" do xit "it always suggests to update pods from external sources" do
podfile = Podfile.new do
platform :ios
pod 'libPusher', :git => "example.com"
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
@resolver.resolve
@resolver.should_install?("libPusher").should.be.true
end end
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment