Commit 33c9710e authored by Fabio Pelosin's avatar Fabio Pelosin

[Install/Update] First complete draft.

parent a4dbe7a4
......@@ -6,7 +6,7 @@ module Pod
$ pod outdated
Shows the dependencies that would be installed by `pod update'. }
Show all of the outdated pods in the current Podfile.lock. }
end
def self.options
......@@ -28,21 +28,51 @@ module Pod
raise Informative, "No `Podfile.lock' found in the current working directory, run `pod install'."
end
# if @update_repo
# print_title 'Updating Spec Repositories', true
# Re"o.new(ARGV.new(["update"])).run
# end
if @update_repo
print_title 'Updating Spec Repositories', true
Repo.new(ARGV.new(["update"])).run
end
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(podfile, lockfile, sandbox)
resolver.update_mode = true
resolver.updated_external_specs = false
resolver.resolve
specs_to_install = resolver.specs_to_install
external_pods = resolver.external_pods
known_update_specs = []
head_mode_specs = []
resolver.specs.each do |s|
next if external_pods.include?(s.name)
next unless specs_to_install.include?(s.name)
if s.version.head?
head_mode_specs << s.name
else
known_update_specs << s.to_s
end
end
if specs_to_install.empty?
puts "\nNo updates are available.\n".yellow
else
puts "\nThe following updates are available:".green
puts " - " << specs_to_install.join("\n - ") << "\n\n"
unless known_update_specs.empty?
puts "\nThe following updates are available:".green
puts " - " << known_update_specs.join("\n - ") << "\n"
end
unless head_mode_specs.empty?
puts "\nThe following pods might present updates as they are in head mode:".green
puts " - " << head_mode_specs.join("\n - ") << "\n"
end
unless (external_pods).empty?
puts "\nThe following pods might present updates as they loaded from an external source:".green
puts " - " << external_pods.join("\n - ") << "\n"
end
puts
end
end
end
......
......@@ -49,7 +49,8 @@ module Pod
@headers_symlink_root ||= "#{project_pods_root}/Headers"
end
# Returns the spec at the pat returned from `project_podfile`.
# @return [Podfile] The Podfile to use for the current execution.
#
def podfile
@podfile ||= begin
Podfile.from_file(project_podfile) if project_podfile.exist?
......@@ -57,6 +58,8 @@ module Pod
end
attr_writer :podfile
# @return [Lockfile] The Lockfile to use for the current execution.
#
def lockfile
@lockfile ||= begin
Lockfile.from_file(project_lockfile) if project_lockfile.exist?
......
......@@ -40,7 +40,7 @@ module Pod
end
def ==(other)
super && (@specification ? @specification == other.specification : @external_source == other.external_source)
super && (head? == other.head?) && (@specification ? @specification == other.specification : @external_source == other.external_source)
end
def subspec_dependency?
......@@ -55,6 +55,16 @@ module Pod
!@external_source.nil?
end
def =~(other)
if head?
name === other.name && other.head?
elsif external?
name === other.name && external_source == other.external_source
else
super && !other.head? && !other.external?
end
end
# In case this is a dependency for a subspec, e.g. 'RestKit/Networking',
# this returns 'RestKit', which is what the Pod::Source needs to know to
# retrieve the correct Set from disk.
......@@ -76,13 +86,14 @@ module Pod
if external?
version << @external_source.description
elsif inline?
version << "defined in Podfile"
version << 'defined in Podfile'
elsif head?
version << 'HEAD'
elsif @version_requirements != Gem::Requirement.default
version << @version_requirements.to_s
end
result = @name.dup
result += " (#{version})" unless version.empty?
result += " [HEAD]" if head?
result << " (#{version})" unless version.empty?
result
end
......@@ -169,7 +180,9 @@ module Pod
class GitSource < AbstractExternalSource
def copy_external_source_into_sandbox(sandbox, platform)
puts " * Pre-downloading: '#{name}'" unless config.silent?
puts "-> Pre-downloading: '#{name}'" unless config.silent?
target = sandbox.root + name
target.rmtree if target.exist?
downloader = Downloader.for_target(sandbox.root + name, @params)
downloader.download
if local_pod = sandbox.installed_pod_named(name, platform)
......@@ -191,7 +204,7 @@ module Pod
def copy_external_source_into_sandbox(sandbox, _)
output_path = sandbox.root + "Local Podspecs/#{name}.podspec"
output_path.dirname.mkpath
puts " * Fetching podspec for `#{name}' from: #{@params[:podspec]}" unless config.silent?
puts "-> Fetching podspec for `#{name}' from: #{@params[:podspec]}" unless config.silent?
open(@params[:podspec]) do |io|
output_path.open('w') { |f| f << io.read }
end
......
......@@ -37,6 +37,11 @@ module Pod
end.compact
end
# Install the pods. If the resolver wants the installation of pod it is done
# even if it exits. In any case if the pod doesn't exits it is installed.
#
# @return [void]
#
def install_dependencies!
pods.each do |pod|
name = pod.top_specification.name
......@@ -49,7 +54,6 @@ module Pod
else
name = pod.to_s
end
name << " [HEAD]" if pod.top_specification.version.head?
puts marker << ( should_install ? "Installing #{name}".green : "Using #{name}" )
end
......@@ -122,7 +126,7 @@ module Pod
puts "- Writing Xcode project file to `#{@sandbox.project_path}'\n\n" if config.verbose?
project.save_as(@sandbox.project_path)
puts "- Writing lockfile in `#{lockfile.defined_in_file}'\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.write_to_disk
......
......@@ -45,16 +45,18 @@ module Pod
attr_accessor :downloaded
alias_method :downloaded?, :downloaded
# @param [Specification] specification
# The first activated specification of the pod.
# @param [Sandbox] sandbox
# The sandbox where the files of the pod will be located.
# @param [Platform] platform
# The platform that will be used to build the pod.
# @param [Specification] specification The first activated specification
# of the pod.
#
# @param [Sandbox] sandbox The sandbox where the files of the
# pod will be located.
#
# @param [Platform] platform The platform that will be used to
# build the pod.
#
# @todo The local pod should be initialized with all the activated
# specifications passed as an array, in order to be able to cache the
# computed values. In other words, it should be immutable.
# specifications passed as an array, in order to be able to cache the
# computed values. In other words, it should be immutable.
#
def initialize(specification, sandbox, platform)
@top_specification, @sandbox, @platform = specification.top_level_parent, sandbox, platform
......@@ -93,7 +95,7 @@ module Pod
end
# @return [String] A string representation of the pod which indicates if
# the pods comes from a local source.
# the pods comes from a local source.
#
def to_s
result = top_specification.to_s
......
module Pod
class Lockfile
# @return [Lockfile] Returns the Lockfile saved in path.
# @return [Lockfile] Returns the Lockfile saved in path. If the
# file could not be loaded or is not compatible with current
# version of CocoaPods {nil}
#
def self.from_file(path)
Lockfile.new(path)
lockfile = Lockfile.new(path)
lockfile.hash_reppresentation ? lockfile : nil
end
# @return [Lockfile] Creates a new Lockfile ready to be saved in path.
......@@ -13,7 +16,7 @@ module Pod
Lockfile.new(path, podfile, specs)
end
attr_reader :defined_in_file, :podfile, :specs, :dictionary_reppresenation
attr_reader :defined_in_file, :podfile, :specs, :hash_reppresentation
# @param [Pathname] the path of the Lockfile.
# If no other value is provided the Lockfile is read from this path.
......@@ -28,36 +31,65 @@ module Pod
else
yaml = YAML.load(File.open(path))
if yaml && Version.new(yaml["COCOAPODS"]) >= Version.new("0.10")
@dictionary_reppresenation = yaml
@hash_reppresentation = yaml
end
end
end
# @return [Array<Dependency>] The dependencies used during the last install.
def pods
return [] unless to_hash
to_hash['PODS'] || []
end
def dependencies
return [] unless to_hash
to_hash['DEPENDENCIES'] || []
end
def external_sources
return [] unless to_hash
to_hash["EXTERNAL SOURCES"] || []
end
# @return [Array<Dependency>] The Podfile dependencies used during the last
# install.
#
def podfile_dependencies
return [] unless to_dict
dependencies = to_dict['DEPENDENCIES'] | []
dependencies.map { |dep|
match_data = dep.match(/(\S*)( (.*))/)
Dependency.new(match_data[1], match_data[2].gsub(/[()]/,''))
}
dependencies.map { |dep| dependency_from_string(dep) }
end
# @return [Array<Dependency>] The dependencies that require exactly,
# the installed pods.
# @return [Array<Dependency>] The dependencies that require the installed
# pods with their exact version.
#
def installed_dependencies
return [] unless to_dict
pods = to_dict['PODS'] | []
pods.map { |pod|
name_and_version = pod.is_a?(String) ? pod : pod.keys[0]
match_data = name_and_version.match(/(\S*)( (.*))/)
Dependency.new(match_data[1], match_data[2].gsub(/[()]/,''))
}
def dependencies_for_pods
pods.map { |pod| dependency_from_string(pod.is_a?(String) ? pod : pod.keys[0]) }
end
# @return [void] Writes the Lockfile to path.
# @return [Dependency] The dependency described by the string.
#
def dependency_from_string(string)
match_data = string.match(/(\S*)( (.*))?/)
name = match_data[1]
version = match_data[2]
version = version.gsub(/[()]/,'') if version
case version
when nil
Dependency.new(name)
when /from `(.*)'/
# @TODO: find a way to serialize the external specs and support
# all the options
external_source_info = external_sources.find {|hash| hash.keys[0] == name} || {}
Dependency.new(name, external_source_info[name])
when /HEAD/
# @TODO: find a way to serialize from the Downloader the information
# necessary to restore a head version.
Dependency.new(name, :head)
else
Dependency.new(name, version)
end
end
# @return [void] Writes the Lockfile to {#path}.
#
def write_to_disk
File.open(defined_in_file, 'w') {|f| f.write(to_yaml) }
......@@ -74,14 +106,15 @@ module Pod
# serialization.
#
def to_yaml
to_dict.to_yaml.gsub(/^--- ?\n/,"")
to_hash.to_yaml.gsub(/^--- ?\n/,"").gsub(/^([A-Z])/,"\n\\1")
end
# @return [Dictionary] The Dictionary representation of the Lockfile.
#
def to_dict
return @dictionary_reppresenation if @dictionary_reppresenation
def to_hash
return @hash_reppresentation if @hash_reppresentation
return nil unless @podfile && @specs
hash = {}
# Get list of [name, dependencies] pairs.
pod_and_deps = specs.map do |spec|
......@@ -100,14 +133,16 @@ module Pod
pod_and_deps = tmp.sort_by(&:first).map do |name, deps|
deps.empty? ? name : {name => deps}
end
hash["PODS"] = pod_and_deps
hash["DEPENDENCIES"] = podfile.dependencies.map{ |d| "#{d}" }.sort
external_sources = podfile.dependencies.select(&:external?).sort{ |d, other| d.name <=> other.name}.map{ |d| { d.name => d.external_source.params } }
hash["EXTERNAL SOURCES"] = external_sources unless external_sources.empty?
dict = {}
dict["PODS"] = pod_and_deps
dict["DEPENDENCIES"] = podfile.dependencies.map(&:to_s).sort
# dict["SPECS_CHECKSUM"] =
# dict["HEAD_SPECS_INFO"] =
dict["COCOAPODS"] = VERSION
dict
# hash["SPECS_CHECKSUM"]
hash["COCOAPODS"] = VERSION
hash
end
end
end
......
......@@ -4,8 +4,42 @@ module Pod
class Resolver
include Config::Mixin
attr_reader :podfile, :lockfile, :sandbox
attr_accessor :cached_sets, :cached_sources, :update_mode
# @return [Podfile] The Podfile used by the resolver.
#
attr_reader :podfile
# @return [Lockfile] The Lockfile used by the resolver.
#
attr_reader :lockfile
# @return [Lockfile] The Sandbox used by the resolver to find external
# dependencies.
#
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
# external sources
#
attr_reader :external_pods
# @return [Array<Set>] The set used to resolve the dependencies.
#
attr_accessor :cached_sets
# @return [Source::Aggregate] A cache of the sources needed to find the
# podspecs.
#
attr_accessor :cached_sources
def initialize(podfile, lockfile, sandbox)
@podfile = podfile
......@@ -14,101 +48,168 @@ module Pod
@cached_sets = {}
@cached_sources = Source::Aggregate.new
@log_indent = 0;
@updated_external_specs = true
end
def installed_dependencies
@installed_dependencies ||= lockfile ? lockfile.installed_dependencies : []
# Identifies the specifications that should be installed according whether
# the resolver is in update mode or not.
#
# @return [void]
#
def resolve
if config.verbose?
unless podfile_dependencies.empty?
puts "\nAlready installed Podfile dependencies detected (Podfile.lock):".green
podfile_dependencies.each {|dependency| puts " - #{dependency}" }
end
unless dependencies_for_pods.empty?
puts "\nInstalled Pods detected (Podfile.lock):".green
dependencies_for_pods.each {|dependency| puts " - #{dependency}" }
end
end
lock_dependencies_version unless update_mode
@cached_specs = {}
@targets_and_specs = {}
@external_pods = []
@podfile.target_definitions.values.each do |target_definition|
puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform}):".green if config.verbose?
@loaded_specs = []
find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
@targets_and_specs[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
end
@cached_specs.values.sort_by(&:name)
@targets_and_specs
end
# @return [Bool] Whether a pod should be installed/reinstalled.
#
def should_install?(name)
names_of_pods_installs = specs_to_install.map(&:name)
names_of_pods_installs.include?(name)
specs_to_install.include?(name)
end
# @return [Array<String>] The list of the names of the pods that need
# to be installed.
#
# - 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
if update_mode
specs = @targets_and_specs.values.flatten
outdated_specs = []
specs.each do |spec|
if spec_outdated?(spec)
outdated_specs << spec
end
end
outdated_specs
else
@specs_to_install ||= begin
specs = @targets_and_specs.values.flatten
outdated_specs = []
to_install = []
specs.each do |spec|
unless spec_installed?(spec)
outdated_specs << spec
if update_mode
# Installation mode
installed_dependency = dependencies_for_pods.find{|d| d.name == spec.name }
outdated = installed_dependency && !installed_dependency.matches_spec?(spec)
head = spec.version.head?
if outdated || head || @external_pods.include?(spec.pod_name)
to_install << spec
end
else
# Installation mode
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
outdated_specs
# Implement this forces the installer to install only the specs that don't have a folder.
# Should install also if there was a change in the podfile
# Disambiguate dependecies before resolution?
[]
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
# @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
# Returns the resolved specifications grouped by target.
#
def specs_by_target
@targets_and_specs
end
def resolve
if lockfile
if config.verbose?
puts "\nInstalled podfile dependencies detected in: #{lockfile.defined_in_file}".green
lockfile.podfile_dependencies.each {|dependency| puts " - #{dependency}" }
puts "\nInstalled pods versions detected in: #{lockfile.defined_in_file}".green
lockfile.installed_dependencies.each {|dependency| puts " - #{dependency}" }
end
# @return [Array<Strings>] The name of the pods that were installed
# but don't have any dependency anymore.
#
def removed_pods
if update_mode
[] # It should never remove any pod in update mode
else
[] # @TODO: Implement
end
end
unless update_mode || !lockfile
puts "\nLocking dependencies to installed versions:".green if config.verbose?
# Add the installed specs which are still compatible with podfile
# requirements to activated
installed_dependencies.each do |dependency|
compatible_with_podfile = dependency && true
if compatible_with_podfile
puts " - #{dependency}" if config.verbose?
set = find_cached_set(dependency, nil)
set.required_by(dependency, lockfile.to_s)
end
end
end
private
@specs = {}
@targets_and_specs = {}
# Locks the version of the previously installed pods if they are still
# compatible and were required by the Podfile.
#
# @return [void]
#
def lock_dependencies_version
return unless lockfile
@dependencies_podfile_incompatible = []
@removed_pods = []
@podfile.target_definitions.values.each do |target_definition|
puts "\nResolving dependencies for target `#{target_definition.name}' (#{target_definition.platform})".green if config.verbose?
@loaded_specs = []
find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
@targets_and_specs[target_definition] = @specs.values_at(*@loaded_specs).sort_by(&:name)
end
puts "\nFinding updated or removed pods:".green if config.verbose?
podfile_deps_names = podfile_dependencies.map(&:name)
@specs.values.sort_by(&:name)
@targets_and_specs
dependencies_for_pods.each do |dependency|
# 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.required_by(dependency, lockfile.to_s)
else
puts " U ".yellow << "#{dependency} -> #{podfile_dependency}" if config.verbose?
@dependencies_podfile_incompatible << dependency
end
end
end
private
def dependency_installed?(dependency)
installed_dependencies.any?{ |installed_dependency| installed_dependency.name == dependency.name }
# @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
def spec_installed?(spec)
installed_dependencies.any?{ |installed_dependency| installed_dependency.name == spec.name }
# @return [Array<Dependency>] Cached copy of the Podfile dependencies used
# during the last install.
#
def podfile_dependencies
@podfile_dependencies ||= lockfile ? lockfile.podfile_dependencies : []
end
def spec_outdated?(spec)
installed_dependency = installed_dependencies.find{|installed_dependency| installed_dependency.name == spec.name }
installed_dependency && !installed_dependency.matches_spec?(spec)
end
# @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
......@@ -118,7 +219,11 @@ module Pod
# The platform isn't actually being used by the LocalPod instance
# that's being used behind the scenes, but passing it anyways for
# completeness sake.
specification = external_source.specification_from_sandbox(@sandbox, platform)
if update_mode && updated_external_specs
specification = external_source.specification_from_external(@sandbox, platform)
else
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
......@@ -130,6 +235,10 @@ module Pod
end
end
# Resolves the dependencies of a specification and stores them in @cached_specs
#
# @return [void]
#
def find_dependency_specs(dependent_specification, dependencies, target_definition)
@log_indent += 1
dependencies.each do |dependency|
......@@ -137,8 +246,10 @@ module Pod
set = find_cached_set(dependency, target_definition.platform)
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 = dependency_installed?(dependency)
mode_wants_spec = dependencies_for_pods.any?{ |d| d.name == dependency.name }
else
mode_wants_spec = true
end
......@@ -146,20 +257,25 @@ module Pod
# Ensure we don't resolve the same spec twice for one target
if mode_wants_spec && !@loaded_specs.include?(dependency.name)
spec = set.specification_by_name(dependency.name)
@external_pods << spec.pod_name if dependency.external?
@loaded_specs << spec.name
@specs[spec.name] = spec
@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
validate_platform!(spec || @specs[dependency.name], target_definition)
validate_platform(spec || @cached_specs[dependency.name], target_definition)
end
end
@log_indent -= 1
end
def validate_platform!(spec, target)
# Ensures that a spec is compatible with 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
......
......@@ -172,6 +172,12 @@ module Pod
end
attr_writer :name
# @return [String] The name of the pod.
#
def pod_name
top_level_parent.name
end
### Attributes that return the first value defined in the chain
def platform
......@@ -441,7 +447,13 @@ module Pod
end
def to_s
"#{name} (#{version})"
if !version
name
elsif version.head?
"#{name} (HEAD from #{version})"
else
"#{name} (#{version})"
end
end
def inspect
......
......@@ -70,14 +70,16 @@ else
end
# Note that we are *not* using the stubbed SpecHelper::Installer subclass.
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = Pod::Installer.new(resolver)
installer.install!
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
installer.lockfile.to_hash.should == {
'PODS' => ['SSToolkit (0.1.3)'],
'DEPENDENCIES' => ["SSToolkit (from `#{url}', commit `#{commit}')"]
'DEPENDENCIES' => ["SSToolkit (from `#{url}', commit `#{commit}')"],
'EXTERNAL SOURCES' => [ {"SSToolkit" => {
:git=>"/Users/fabio/Documents/GitHub/CP/CocoaPods/spec/fixtures/integration/sstoolkit", :commit=>"2adcd0f81740d6b0cd4589af98790eee3bd1ae7b"
}}],
'COCOAPODS' => Pod::VERSION
}
end
......@@ -95,10 +97,12 @@ else
installer = SpecHelper::Installer.new(resolver)
installer.install!
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
installer.lockfile.to_hash.should == {
'PODS' => [ 'Reachability (1.2.3)' ],
# 'DOWNLOAD_ONLY' => ["ASIHTTPRequest (1.8.1)"],
'DEPENDENCIES' => ["Reachability (from `#{url}')"]
'DEPENDENCIES' => ["Reachability (from `#{url}')"],
"EXTERNAL SOURCES"=>[{"Reachability"=>{
:podspec=>"https://raw.github.com/gist/1349824/3ec6aa60c19113573fc48eac19d0fafd6a69e033/Reachability.podspec"}}],
'COCOAPODS' => Pod::VERSION
}
end
......@@ -170,7 +174,7 @@ else
installer = SpecHelper::Installer.new(resolver)
installer.install!
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
installer.lockfile.to_hash.tap {|d| d.delete("COCOAPODS") }.should == {
'PODS' => ['JSONKit (1.2)', 'SSZipArchive (0.1.0)'],
'DEPENDENCIES' => ["JSONKit (defined in Podfile)", "SSZipArchive (defined in Podfile)"]
}
......@@ -200,7 +204,7 @@ else
installer.install!
# TODO might be nicer looking to not show the dependencies of the top level spec for each subspec (Reachability).
installer.lockfile.to_dict.should == {
installer.lockfile.to_hash.should == {
"PODS" => [{ "ASIHTTPRequest (1.8.1)" => ["ASIHTTPRequest/ASIWebPageRequest (= 1.8.1)",
"ASIHTTPRequest/CloudFiles (= 1.8.1)",
"ASIHTTPRequest/S3 (= 1.8.1)",
......@@ -258,7 +262,7 @@ else
podfile = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
pod 'SSZipArchive'
pod 'SSZipArchive', '0.1.0'
post_install do |installer|
target = installer.project.targets.first
......@@ -277,15 +281,13 @@ else
end
# TODO add a simple source file which uses the compiled lib to check that it really really works
# TODO update for specification refactor
it "activates required pods and create a working static library xcode project" do
podfile = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
pod 'Reachability', '> 2.0.5' if platform == :ios
# pod 'ASIWebPageRequest', '>= 1.8.1'
pod 'JSONKit', '>= 1.0'
pod 'SSZipArchive', '< 2'
pod 'SSZipArchive', '< 0.1.2'
end
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
......@@ -294,17 +296,14 @@ else
lockfile_contents = {
'PODS' => [
# { 'ASIHTTPRequest (1.8.1)' => ["Reachability"] },
# { 'ASIWebPageRequest (1.8.1)' => ["ASIHTTPRequest (= 1.8.1)"] },
'JSONKit (1.5pre)',
'Reachability (3.0.0)',
'SSZipArchive (0.2.2)',
'SSZipArchive (0.1.1)',
],
'DEPENDENCIES' => [
# "ASIWebPageRequest (>= 1.8.1)",
"JSONKit (>= 1.0)",
"Reachability (> 2.0.5)",
"SSZipArchive (< 2)",
"SSZipArchive (< 0.1.2)",
],
"COCOAPODS" => Pod::VERSION
}
......@@ -314,7 +313,7 @@ else
lockfile_contents['PODS'].delete_at(1)
# lockfile_contents['PODS'][0] = 'ASIHTTPRequest (1.8.1)'
end
installer.lockfile.to_dict.should == lockfile_contents
installer.lockfile.to_hash.should == lockfile_contents
root = config.project_pods_root
(root + 'Pods.xcconfig').read.should == installer.target_installers.first.xcconfig.to_s
......@@ -325,7 +324,6 @@ else
end
if platform == :ios
# TODO: update for Specification Refactor
it "does not activate pods that are only part of other pods" do
spec = Pod::Podfile.new do
self.platform platform
......@@ -337,7 +335,7 @@ else
installer = SpecHelper::Installer.new(resolver)
installer.install!
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
installer.lockfile.to_hash.tap {|d| d.delete("COCOAPODS") }.should == {
# 'PODS' => [{ 'Reachability (2.0.4)' => ["ASIHTTPRequest (>= 1.8)"] }],
'PODS' => [ 'Reachability (2.0.4)' ],
# 'DOWNLOAD_ONLY' => ["ASIHTTPRequest (1.8.1)"],
......@@ -350,7 +348,7 @@ else
spec = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
pod 'SSZipArchive'
pod 'SSZipArchive', '0.1.0'
end
resolver = Pod::Resolver.new(spec, nil, Pod::Sandbox.new(config.project_pods_root))
......@@ -378,7 +376,7 @@ else
spec = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
pod 'SSZipArchive'
pod 'SSZipArchive', '0.1.0'
end
resolver = Pod::Resolver.new(spec, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
......@@ -392,7 +390,7 @@ else
podfile = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
target(:debug) { pod 'SSZipArchive' }
target(:debug) { pod 'SSZipArchive', '0.1.0' }
target(:test, :exclusive => true) { pod 'JSONKit' }
pod 'ASIHTTPRequest'
end
......@@ -447,7 +445,7 @@ else
podfile = Pod::Podfile.new do
self.platform platform
xcodeproj projpath
pod 'SSZipArchive'
pod 'SSZipArchive', '0.1.0'
end
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
......@@ -473,7 +471,7 @@ else
podfile = Pod::Podfile.new do
self.platform platform
xcodeproj 'dummy'
target(:debug) { pod 'SSZipArchive' }
target(:debug) { pod 'SSZipArchive', '0.1.0' }
target(:test, :exclusive => true) { pod 'JSONKit' }
pod 'ASIHTTPRequest'
end
......
......@@ -37,7 +37,7 @@ module Pod
dep2 = Dependency.new { |s| s.name = 'bananas'; s.version = '1' }
dep1.should == dep2
end
it 'raises if created without either valid name/version/external requirements or a block' do
lambda { Dependency.new }.should.raise Informative
end
......@@ -49,11 +49,11 @@ module Pod
spec.version = "1.0.3"
end
end
it 'it identifies itself as an inline dependency' do
@dependency.should.be.inline
end
it 'attaches a custom spec to the dependency, configured by the block' do
@dependency.specification.name.should == "my-custom-spec"
end
......@@ -73,7 +73,7 @@ module Pod
it "identifies itself as a `bleeding edge' dependency" do
dependency = Dependency.new("cocoapods", :head)
dependency.should.be.head
dependency.to_s.should == "cocoapods [HEAD]"
dependency.to_s.should == "cocoapods (HEAD)"
end
it "only supports the `:head' option on the last version of a pod" do
......@@ -98,7 +98,7 @@ module Pod
Downloader.stubs(:for_target).returns(stub_everything)
pod = mock('LocaPod', :downloaded= => true)
sandbox = stub('Sandbox', :root => '', :installed_pod_named => pod)
sandbox = stub('Sandbox', :root => temporary_sandbox.root, :installed_pod_named => pod)
@dependency.external_source.copy_external_source_into_sandbox(sandbox, Platform.ios)
end
end
......
......@@ -42,7 +42,7 @@ describe "Pod::Lockfile" do
File.open(tmp_path, 'w') {|f| f.write(sample) }
lockfile = Pod::Lockfile.from_file(tmp_path)
lockfile.defined_in_file.should == tmp_path
lockfile.to_dict.should == YAML.load(sample)
lockfile.to_hash.should == YAML.load(sample)
end
before do
......@@ -54,7 +54,7 @@ describe "Pod::Lockfile" do
end
it "generates a valid Dictionary representation" do
@lockfile.to_dict.should == YAML.load(sample)
@lockfile.to_hash.should == YAML.load(sample)
end
it "returns the Podfile dependencies" do
......@@ -64,7 +64,7 @@ describe "Pod::Lockfile" do
end
it "returns the dependencies for the installed pods" do
@lockfile.installed_dependencies.should == [
@lockfile.dependencies_for_pods.should == [
Pod::Dependency.new("BananaLib", "= 1.0"),
Pod::Dependency.new("monkey", "= 1.0.8")
]
......@@ -73,7 +73,7 @@ describe "Pod::Lockfile" do
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.to_dict.should == nil
lockfile.should == nil
end
it "serializes correctly `:head' dependencies" do
......@@ -93,13 +93,13 @@ describe "Pod::Lockfile" do
]
specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.create(tmp_path, podfile, specs)
lockfile.to_dict["DEPENDENCIES"][0].should == "BananaLib [HEAD]"
lockfile.to_hash["DEPENDENCIES"][0].should == "BananaLib (HEAD)"
end
it "serializes correctly external dependencies" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BananaLib', :git => "www.example.com"
pod 'BananaLib', { :git => "www.example.com", :tag => '1.0' }
end
specs = [
Pod::Specification.new do |s|
......@@ -113,12 +113,36 @@ describe "Pod::Lockfile" do
]
specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.create(tmp_path, podfile, specs)
lockfile.to_dict["DEPENDENCIES"][0].should == "BananaLib (from `www.example.com')"
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' }
end
xit "reads `:heads' dependencies correctly" do
it "creates a dependency from a string" do
d = @lockfile.dependency_from_string("BananaLib (1.0)")
d.name.should == "BananaLib"
d.requirement.should =~ Pod::Version.new("1.0")
d.head.should.be.nil
d.external_source.should.be.nil
end
xit "reads external dependencies dependencies correctly" do
it "creates a head dependency from a string" do
d = @lockfile.dependency_from_string("BananaLib (HEAD)")
d.name.should == "BananaLib"
d.requirement.should.be.none?
d.head.should.be.true
d.external_source.should.be.nil
end
it "creates an external dependency from a string" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BananaLib', { :git => "www.example.com", :tag => '1.0' }
end
lockfile = Pod::Lockfile.create(tmp_path, podfile, [])
d = lockfile.dependency_from_string("BananaLib (from `www.example.com', tag `1.0')")
d.name.should == "BananaLib"
d.requirement.should.be.none?
d.external?.should.be.true
d.external_source.description.should == "from `www.example.com', tag `1.0'"
end
end
......@@ -191,42 +191,175 @@ module Pod
jsonkit.version.should.be.head
end
xit "raises if it finds two conflicting dependencies" do
it "accepts a nil lockfile" do
lambda { Resolver.new(@podfile, nil, stub('sandbox'))}.should.not.raise
end
it "raises if it finds two conflicting dependencies" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', "1.4"
pod 'JSONKit', "1.5pre"
end
resolver = Resolver.new(podfile, nil, stub('sandbox'))
lambda {resolver.resolve}.should.raise Pod::Informative
end
describe "Concerning the Lockfile" do
xit "accepts a nil lockfile" do
lambda { Resolver.new(@podfile, nil, stub('sandbox'))}.should.not.raise
describe "Concerning Installation mode" do
before do
config.repos_dir = fixture('spec-repos')
@podfile = 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 = Lockfile.create(nil, @podfile, @specs)
@resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
end
xit "detects the pods that need to be installed" do
it "doesn't install pods still compatible with the Podfile" do
@resolver.resolve
@resolver.should_install?("BlocksKit").should.be.false
@resolver.should_install?("JSONKit").should.be.false
end
it "doesn't updates pods still compatible with the Podfile" do
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (1.4)"
end
xit "detects the pods that don't need to be installed" do
it "doesn't include pods removed from the Podfile" do
podfile = Podfile.new { platform :ios; pod 'JSONKit' }
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve.values.flatten.map(&:name).should == %w{ JSONKit }
end
it "reinstalls pods updated in the Podfile" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', '1.5pre'
pod 'BlocksKit'
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "BlocksKit (1.0.0)"
installed.should.include? "JSONKit (1.5pre)"
end
xit "detects the pods that can be updated" do
it "installs pods added to the Podfile" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit'
pod 'BlocksKit'
pod 'libPusher'
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "libPusher (1.3)"
end
it "handles head pods" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', :head # Existing pod switched to head mode
pod 'libPusher', :head # New pod
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve
@resolver.should_install?("JSONKit").should.be.true
@resolver.should_install?("libPusher").should.be.true
end
xit "doesn't install new pods in `update_mode'" do
it "handles pods from external dependencies" do
podfile = Podfile.new do
platform :ios
pod 'libPusher', :git => 'GIT-URL'
end
spec = Spec.new do |s|
s.name = 'libPusher'
s.version = '1.3'
end
podfile.dependencies.first.external_source.stubs(:specification_from_sandbox).returns(spec)
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.resolve
@resolver.should_install?("JSONKit").should.be.false
end
end
describe "Concerning Update mode" do
before do
config.repos_dir = fixture('spec-repos')
@podfile = Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit'
pod 'libPusher'
end
@specs = [
Pod::Specification.new do |s|
s.name = "libPusher"
s.version = "1.3"
end,
Pod::Specification.new do |s|
s.name = "JSONKit"
s.version = "1.4"
end ]
@specs.each { |s| s.activate_platform(:ios) }
@lockfile = Lockfile.create(nil, @podfile, @specs)
@resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
end
xit "handles correctly pods with external source" do
it "identifies the pods that can be updated" do
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (1.5pre)"
@resolver.should_install?("JSONKit").should.be.true
end
it "respecs the constraints of the pofile" do
podfile = Podfile.new do
platform :ios
pod 'BlocksKit'
pod 'JSONKit', '1.4'
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.should.include? "JSONKit (1.4)"
@resolver.should_install?("JSONKit").should.be.false
end
xit "it always suggest to update pods in head mode" do
it "doesn't install new pods in `update_mode'" do
installed = @resolver.resolve.values.flatten.map(&:to_s)
installed.join(' ').should.not.include?('BlocksKit')
@resolver.should_install?("BlocksKit").should.be.false
end
it "it always suggests to update pods in head mode" do
podfile = Podfile.new do
platform :ios
pod 'libPusher', :head
end
@resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
@resolver.update_mode = true
@resolver.resolve
@resolver.should_install?("libPusher").should.be.true
end
xit "it prevents a pod from upgrading during an install" do
xit "it suggests to update pods from external sources" do
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