Skip to content
Browse files

[Install/Update] First complete draft.

  • Loading branch information...
1 parent a4dbe7a commit 33c9710ea75b6e3ff7f50314ce31c0619b1649ba @fabiopelosin fabiopelosin committed Aug 9, 2012
View
44 lib/cocoapods/command/outdated.rb
@@ -6,7 +6,7 @@ def self.banner
$ 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 @@ def run
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
View
5 lib/cocoapods/config.rb
@@ -49,14 +49,17 @@ def headers_symlink_root
@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?
end
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?
View
25 lib/cocoapods/dependency.rb
@@ -40,7 +40,7 @@ def latest_version?
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 @@ def external?
!@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 @@ def to_s
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 @@ def ==(other_source)
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 @@ class PodspecSource < AbstractExternalSource
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
View
8 lib/cocoapods/installer.rb
@@ -37,6 +37,11 @@ def target_installers
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 @@ def install_dependencies!
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 @@ def install!
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
View
20 lib/cocoapods/local_pod.rb
@@ -45,16 +45,18 @@ class LocalPod
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 @@ def root
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
View
99 lib/cocoapods/lockfile.rb
@@ -1,10 +1,13 @@
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 @@ def self.create(path, podfile, specs)
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 @@ def initialize(path, podfile = nil, specs = nil)
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 @@ def to_s
# 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 @@ def to_dict
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
View
258 lib/cocoapods/resolver.rb
@@ -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 @@ def initialize(podfile, lockfile, sandbox)
@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 @@ def find_cached_set(dependency, platform)
# 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,36 +235,47 @@ def find_cached_set(dependency, platform)
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|
puts ' ' * @log_indent + "- #{dependency}" if config.verbose?
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
# 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
View
14 lib/cocoapods/specification.rb
@@ -172,6 +172,12 @@ def name
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 @@ def dependency_by_top_level_spec_name(name)
end
def to_s
- "#{name} (#{version})"
+ if !version
+ name
+ elsif version.head?
+ "#{name} (HEAD from #{version})"
+ else
+ "#{name} (#{version})"
+ end
end
def inspect
View
50 spec/integration_spec.rb
@@ -70,14 +70,16 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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 @@ def should_xcodebuild(target_definition)
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
View
10 spec/unit/dependency_spec.rb
@@ -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
View
42 spec/unit/lockfile_spec.rb
@@ -42,7 +42,7 @@ def tmp_path
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 @@ def tmp_path
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 @@ def tmp_path
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 @@ def tmp_path
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 @@ def tmp_path
]
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 @@ def tmp_path
]
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
View
155 spec/unit/resolver_spec.rb
@@ -191,42 +191,175 @@ def set.specification; spec = super; spec.platform = @stubbed_platform; spec; en
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

0 comments on commit 33c9710

Please sign in to comment.
Something went wrong with that request. Please try again.