Commit 34a68534 authored by Fabio Pelosin's avatar Fabio Pelosin

Introduced Pod `update`, `outdated`.

See #131, #191.

- The installer is initialized with a resolver. The resolver is responsible of
  indicating which specs must be installed/reinstalled.
- It was introduced a slight change in the format of the Podfile.lock.
- The specification set was simplified to receive and handle Pod::Dependency
  instead of Pod::Specification. With this change it also appears to be more
  robust.

A this stage it appears to be working. However the support, for external and
head dependencies is weak.
parent 02232337
......@@ -32,6 +32,7 @@ module Pod
autoload :Executable, 'cocoapods/executable'
autoload :Installer, 'cocoapods/installer'
autoload :LocalPod, 'cocoapods/local_pod'
autoload :Lockfile, 'cocoapods/lockfile'
autoload :Platform, 'cocoapods/platform'
autoload :Podfile, 'cocoapods/podfile'
autoload :Project, 'cocoapods/project'
......
......@@ -6,12 +6,14 @@ module Pod
autoload :Install, 'cocoapods/command/install'
autoload :List, 'cocoapods/command/list'
autoload :Linter, 'cocoapods/command/linter'
autoload :Outdated, 'cocoapods/command/outdated'
autoload :Presenter, 'cocoapods/command/presenter'
autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo'
autoload :Search, 'cocoapods/command/search'
autoload :Setup, 'cocoapods/command/setup'
autoload :Spec, 'cocoapods/command/spec'
autoload :Update, 'cocoapods/command/update'
class Help < Informative
def initialize(command_class, argv)
......@@ -100,12 +102,14 @@ module Pod
command_class = case argv.shift_argument
when 'install' then Install
when 'list' then List
when 'outdated' then Outdated
when 'push' then Push
when 'repo' then Repo
when 'search' then Search
when 'list' then List
when 'setup' then Setup
when 'spec' then Spec
when 'push' then Push
when 'update' then Update
end
if show_help || command_class.nil?
......
......@@ -6,7 +6,7 @@ module Pod
$ pod install
Downloads all dependencies defined in `Podfile' and creates an Xcode
Downloads all dependencies updated in `Podfile' and creates an Xcode
Pods library project in `./Pods'.
The Xcode project file should be specified in your `Podfile` like this:
......@@ -50,7 +50,9 @@ module Pod
Repo.new(ARGV.new(["update"])).run
end
Installer.new(podfile).install!
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(podfile, config.lockfile, sandbox)
Installer.new(resolver).install!
end
end
end
......
......@@ -103,7 +103,9 @@ module Pod
def install_pod
podfile = podfile_from_spec
config.verbose
installer = Installer.new(podfile)
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(podfile, nil, sandbox)
installer = Installer.new(resolver)
installer.install!
@pod = installer.pods.find { |pod| pod.top_specification == spec }
config.silent
......
module Pod
class Command
class Outdated < Command
def self.banner
%{Updates dependencies of a project:
$ pod outdated
Shows the dependencies that would be installed by `pod update'. }
end
def self.options
[
["--no-update", "Skip running `pod repo update` before install"],
].concat(super)
end
def initialize(argv)
@update_repo = !argv.option('--no-update')
super unless argv.empty?
end
def run
unless podfile = config.podfile
raise Informative, "No `Podfile' found in the current working directory."
end
unless lockfile = config.lockfile
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
sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(podfile, lockfile, sandbox)
resolver.update_mode = true
resolver.resolve
specs_to_install = resolver.specs_to_install
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"
end
end
end
end
end
module Pod
class Command
class Update < Command
def self.banner
%{Updates dependencies of a project:
$ pod update
Updates all dependencies installed by `pod install`. It doesn't
install new dependencies. }
end
def self.options
[
["--no-clean", "Leave SCM dirs like `.git' and `.svn' intact after downloading"],
["--no-doc", "Skip documentation generation with appledoc"],
["--no-integrate", "Skip integration of the Pods libraries in the Xcode project(s)"],
["--no-update", "Skip running `pod repo update` before install"],
].concat(super)
end
def initialize(argv)
config.clean = !argv.option('--no-clean')
config.generate_docs = !argv.option('--no-doc')
config.integrate_targets = !argv.option('--no-integrate')
@update_repo = !argv.option('--no-update')
super unless argv.empty?
end
def run
unless podfile = config.podfile
raise Informative, "No `Podfile' found in the current working directory."
end
unless lockfile = config.lockfile
raise Informative, "No `Podfile.lock' found in the current working directory, run `pod install'."
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
Installer.new(resolver).install!
end
end
end
end
......@@ -41,6 +41,10 @@ module Pod
@project_podfile ||= project_root + 'Podfile'
end
def project_lockfile
@project_lockfile ||= project_root + 'Podfile.lock'
end
def headers_symlink_root
@headers_symlink_root ||= "#{project_pods_root}/Headers"
end
......@@ -53,6 +57,12 @@ module Pod
end
attr_writer :podfile
def lockfile
@lockfile ||= begin
Lockfile.from_file(project_lockfile) if project_lockfile.exist?
end
end
module Mixin
def config
Config.instance
......
......@@ -7,19 +7,12 @@ module Pod
include Config::Mixin
attr_reader :sandbox
attr_reader :resolver, :sandbox, :lockfile
def initialize(podfile)
@podfile = podfile
# FIXME: pass this into the installer as a parameter
@sandbox = Sandbox.new(config.project_pods_root)
@resolver = Resolver.new(@podfile, @sandbox)
# TODO: remove in 0.7 (legacy support for config.ios? and config.osx?)
config.podfile = podfile
end
def lock_file
config.project_root + 'Podfile.lock'
def initialize(resolver)
@resolver = resolver
@podfile = resolver.podfile
@sandbox = resolver.sandbox
end
def project
......@@ -46,6 +39,9 @@ module Pod
def install_dependencies!
pods.each do |pod|
name = pod.top_specification.name
should_install = @resolver.should_install?(name) || !pod.exists?
unless config.silent?
marker = config.verbose ? "\n-> ".green : ''
if subspec_name = pod.top_specification.preferred_dependency
......@@ -54,11 +50,12 @@ module Pod
name = pod.to_s
end
name << " [HEAD]" if pod.top_specification.version.head?
puts marker << ( pod.exists? ? "Using #{name}" : "Installing #{name}".green )
puts marker << ( should_install ? "Installing #{name}".green : "Using #{name}" )
end
download_pod(pod) unless pod.exists?
if should_install
pod.implode
download_pod(pod)
# This will not happen if the pod existed before we started the install
# process.
if pod.downloaded?
......@@ -71,6 +68,7 @@ module Pod
end
end
end
end
def download_pod(pod)
downloader = Downloader.for_pod(pod)
......@@ -117,8 +115,6 @@ module Pod
generate_dummy_source(target_installer)
end
generate_lock_file!(specifications)
puts "- Running post install hooks" if config.verbose?
# Post install hooks run _before_ saving of project, so that they can alter it before saving.
run_post_install_hooks
......@@ -126,6 +122,10 @@ 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?
@lockfile = Lockfile.create(config.project_lockfile, @podfile, specs_by_target.values.flatten)
@lockfile.write_to_disk
UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets?
end
......@@ -133,7 +133,7 @@ module Pod
# we loop over target installers instead of pods, because we yield the target installer
# to the spec post install hook.
target_installers.each do |target_installer|
specs_by_target[target_installer.target_definition].each do |spec|
@specs_by_target[target_installer.target_definition].each do |spec|
spec.post_install(target_installer)
end
end
......@@ -141,44 +141,6 @@ module Pod
@podfile.post_install!(self)
end
def generate_lock_file!(specs)
lock_file.open('w') do |file|
file.puts "PODS:"
# Get list of [name, dependencies] pairs.
pod_and_deps = specs.map do |spec|
[spec.to_s, spec.dependencies.map(&:to_s).sort]
end.uniq
# Merge dependencies of ios and osx version of the same pod.
tmp = {}
pod_and_deps.each do |name, deps|
if tmp[name]
tmp[name].concat(deps).uniq!
else
tmp[name] = deps
end
end
pod_and_deps = tmp
# Sort by name and print
pod_and_deps.sort_by(&:first).each do |name, deps|
if deps.empty?
file.puts " - #{name}"
else
file.puts " - #{name}:"
deps.each { |dep| file.puts " - #{dep}" }
end
end
file.puts
file.puts "DEPENDENCIES:"
@podfile.dependencies.map(&:to_s).sort.each do |dep|
file.puts " - #{dep}"
end
end
end
def generate_dummy_source(target_installer)
class_name_identifier = target_installer.target_definition.label
dummy_source = Generator::DummySource.new(class_name_identifier)
......@@ -192,15 +154,6 @@ module Pod
target_installer.target.source_build_phases.first << project_file
end
def specs_by_target
@specs_by_target ||= @resolver.resolve
end
# @return [Array<Specification>] All dependencies that have been resolved.
def specifications
specs_by_target.values.flatten
end
# @return [Array<LocalPod>] A list of LocalPod instances for each
# dependency that is not a download-only one.
def pods
......@@ -219,6 +172,14 @@ module Pod
result
end
def specifications
specs_by_target.values.flatten
end
def specs_by_target
@specs_by_target ||= @resolver.resolve
end
private
def print_title(title, only_verbose = true)
......
module Pod
class Lockfile
# @return [Lockfile] Returns the Lockfile saved in path.
#
def self.from_file(path)
Lockfile.new(path)
end
# @return [Lockfile] Creates a new Lockfile ready to be saved in path.
#
def self.create(path, podfile, specs)
Lockfile.new(path, podfile, specs)
end
attr_reader :defined_in_file, :podfile, :specs, :dictionary_reppresenation
# @param [Pathname] the path of the Lockfile.
# If no other value is provided the Lockfile is read from this path.
# @param [Podfile] the Podfile to use for generating the Lockfile.
# @param [specs] the specs installed.
#
def initialize(path, podfile = nil, specs = nil)
@defined_in_file = path
if podfile && specs
@podfile = podfile
@specs = specs
else
yaml = YAML.load(File.open(path))
if yaml && Version.new(yaml["COCOAPODS"]) >= Version.new("0.10")
@dictionary_reppresenation = yaml
end
end
end
# @return [Array<Dependency>] The 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(/[()]/,''))
}
end
# @return [Array<Dependency>] The dependencies that require exactly,
# the installed pods.
#
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(/[()]/,''))
}
end
# @return [void] Writes the Lockfile to path.
#
def write_to_disk
File.open(defined_in_file, 'w') {|f| f.write(to_yaml) }
end
# @return [String] A string useful to represent the Lockfile in a message
# presented to the user.
#
def to_s
"Podfile.lock"
end
# @return [String] The YAML representation of the Lockfile, used for
# serialization.
#
def to_yaml
to_dict.to_yaml
end
# @return [Dictionary] The Dictionary representation of the Lockfile.
#
def to_dict
return @dictionary_reppresenation if @dictionary_reppresenation
return nil unless @podfile && @specs
# Get list of [name, dependencies] pairs.
pod_and_deps = specs.map do |spec|
[spec.to_s, spec.dependencies.map(&:to_s).sort]
end.uniq
# Merge dependencies of ios and osx version of the same pod.
tmp = {}
pod_and_deps.each do |name, deps|
if tmp[name]
tmp[name].concat(deps).uniq!
else
tmp[name] = deps
end
end
pod_and_deps = tmp.sort_by(&:first).map do |name, deps|
deps.empty? ? name : {name => deps}
end
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
end
end
end
......@@ -4,34 +4,111 @@ module Pod
class Resolver
include Config::Mixin
attr_reader :podfile, :sandbox
attr_accessor :cached_sets, :cached_sources
attr_reader :podfile, :lockfile, :sandbox
attr_accessor :cached_sets, :cached_sources, :update_mode
def initialize(podfile, sandbox)
def initialize(podfile, lockfile, sandbox)
@podfile = podfile
@lockfile = lockfile
@sandbox = sandbox
@cached_sets = {}
@cached_sources = Source::Aggregate.new
@log_indent = 0;
end
def installed_dependencies
@installed_dependencies ||= lockfile ? lockfile.installed_dependencies : []
end
def should_install?(name)
names_of_pods_installs = specs_to_install.map(&:name)
names_of_pods_installs.include?(name)
end
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 = @targets_and_specs.values.flatten
outdated_specs = []
specs.each do |spec|
unless spec_installed?(spec)
outdated_specs << spec
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?
[]
end
end
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
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
@specs = {}
targets_and_specs = {}
@targets_and_specs = {}
@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)
@targets_and_specs[target_definition] = @specs.values_at(*@loaded_specs).sort_by(&:name)
end
@specs.values.sort_by(&:name)
targets_and_specs
@targets_and_specs
end
private
def dependency_installed?(dependency)
installed_dependencies.any?{ |installed_dependency| installed_dependency.name == dependency.name }
end
def spec_installed?(spec)
installed_dependencies.any?{ |installed_dependency| installed_dependency.name == spec.name }
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
def find_cached_set(dependency, platform)
set_name = dependency.name.split('/').first
@cached_sets[set_name] ||= begin
......@@ -58,9 +135,16 @@ module Pod
dependencies.each do |dependency|
puts ' ' * @log_indent + "- #{dependency}" if config.verbose?
set = find_cached_set(dependency, target_definition.platform)
set.required_by(dependent_specification)
set.required_by(dependency, dependent_specification.to_s)
if update_mode
mode_wants_spec = dependency_installed?(dependency)
else
mode_wants_spec = true
end
# Ensure we don't resolve the same spec twice for one target
unless @loaded_specs.include?(dependency.name)
if mode_wants_spec && !@loaded_specs.include?(dependency.name)
spec = set.specification_by_name(dependency.name)
@loaded_specs << spec.name
@specs[spec.name] = spec
......@@ -69,9 +153,9 @@ module Pod
spec.version.head = dependency.head?
# And recursively load the dependencies of the spec.
find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
end
validate_platform!(spec || @specs[dependency.name], target_definition)
end
end
@log_indent -= 1
end
......
......@@ -6,22 +6,18 @@ module Pod
def initialize(pod_dir)
@pod_dir = pod_dir
@required_by = []
@dependencies = []
end
def required_by(specification)
# Skip subspecs because the can't require a different version of the top level parent
return if !specification.podfile? && specification.top_level_parent.name == name
dependency = specification.dependency_by_top_level_spec_name(name)
# TODO we don’t actually do anything in our Version subclass. Maybe we should just remove that.
def required_by(dependency, dependent_name)
unless @required_by.empty? || dependency.requirement.satisfied_by?(Gem::Version.new(required_version.to_s))
# TODO add graph that shows which dependencies led to this.
raise Informative, "#{specification} tries to activate `#{dependency}', " \
raise Informative, "#{dependent_name} tries to activate `#{dependency}', " \
"but already activated version `#{required_version}' " \
"by #{@required_by.join(', ')}."
end
@specification = nil
@required_by << specification
@required_by << dependent_name
@dependencies << dependency
end
def specification_by_name(name)
......@@ -29,8 +25,8 @@ module Pod
end
def dependency
@required_by.inject(Dependency.new(name)) do |previous, spec|
previous.merge(spec.dependency_by_top_level_spec_name(name).to_top_level_spec_dependency)
@dependencies.inject(Dependency.new(name)) do |previous, dependency|
previous.merge(dependency.to_top_level_spec_dependency)
end
end
......@@ -74,6 +70,7 @@ module Pod
def initialize(specification)
@specification = specification
@required_by = []
@dependencies = []
end
def name
......@@ -84,9 +81,9 @@ module Pod
self.class === other && name == other.name
end
def required_by(specification)
def required_by(dependency, dependent_name)
before = @specification
super(specification)
super(dependency, dependent_name)
ensure
@specification = before
end
......
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Command::Outdated do
extend SpecHelper::Command
extend SpecHelper::TemporaryDirectory
extend SpecHelper::TemporaryRepos
it "tells the user that no Podfile was found in the current working dir" do
exception = lambda { run_command('outdated','--no-update') }.should.raise Pod::Informative
exception.message.should.include "No `Podfile' found in the current working directory."
end
it "tells the user that no Lockfile was found in the current working dir" do
file = temporary_directory + 'Podfile'
File.open(file, 'w') {|f| f.write('platform :ios') }
Dir.chdir(temporary_directory) do
exception = lambda { run_command('outdated','--no-update') }.should.raise Pod::Informative
exception.message.should.include "No `Podfile.lock' found in the current working directory"
end
end
end
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Command::Update do
extend SpecHelper::Command
extend SpecHelper::TemporaryDirectory
extend SpecHelper::TemporaryRepos
it "tells the user that no Podfile was found in the current working dir" do
exception = lambda { run_command('update','--no-update') }.should.raise Pod::Informative
exception.message.should.include "No `Podfile' found in the current working directory."
end
it "tells the user that no Lockfile was found in the current working dir" do
file = temporary_directory + 'Podfile'
File.open(file, 'w') {|f| f.write('platform :ios') }
Dir.chdir(temporary_directory) do
exception = lambda { run_command('update','--no-update') }.should.raise Pod::Informative
exception.message.should.include "No `Podfile.lock' found in the current working directory"
end
end
end
......@@ -70,10 +70,12 @@ else
end
# Note that we are *not* using the stubbed SpecHelper::Installer subclass.
installer = Pod::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = Pod::Installer.new(resolver)
installer.install!
YAML.load(installer.lock_file.read).should == {
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
'PODS' => ['SSToolkit (0.1.3)'],
'DEPENDENCIES' => ["SSToolkit (from `#{url}', commit `#{commit}')"]
}
......@@ -88,10 +90,12 @@ else
pod 'Reachability', :podspec => url
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
YAML.load(installer.lock_file.read).should == {
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
'PODS' => [ 'Reachability (1.2.3)' ],
# 'DOWNLOAD_ONLY' => ["ASIHTTPRequest (1.8.1)"],
'DEPENDENCIES' => ["Reachability (from `#{url}')"]
......@@ -111,7 +115,8 @@ else
end
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
dummy = (config.project_pods_root + 'PodsDummy_Pods.m').read
......@@ -134,7 +139,8 @@ else
end
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
dummy = (config.project_pods_root + 'PodsDummy_Pods_AnotherTarget.m').read
......@@ -160,10 +166,11 @@ else
end
Pod::Specification.any_instance.stubs(:preserve_paths).returns(['CHANGELOG.md'])
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
YAML.load(installer.lock_file.read).should == {
installer.lockfile.to_dict.tap {|d| d.delete("COCOAPODS") }.should == {
'PODS' => ['JSONKit (1.2)', 'SSZipArchive (0.1.0)'],
'DEPENDENCIES' => ["JSONKit (defined in Podfile)", "SSZipArchive (defined in Podfile)"]
}
......@@ -188,11 +195,12 @@ else
end
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
# TODO might be nicer looking to not show the dependencies of the top level spec for each subspec (Reachability).
YAML.load(installer.lock_file.read).should == {
installer.lockfile.to_dict.should == {
"PODS" => [{ "ASIHTTPRequest (1.8.1)" => ["ASIHTTPRequest/ASIWebPageRequest (= 1.8.1)",
"ASIHTTPRequest/CloudFiles (= 1.8.1)",
"ASIHTTPRequest/S3 (= 1.8.1)",
......@@ -201,7 +209,8 @@ else
{ "ASIHTTPRequest/CloudFiles (1.8.1)" => ["Reachability"] },
{ "ASIHTTPRequest/S3 (1.8.1)" => ["Reachability"] },
"JSONKit (1.4)", "Reachability (3.0.0)"],
"DEPENDENCIES" => ["ASIHTTPRequest", "JSONKit (= 1.4)"]
"DEPENDENCIES" => ["ASIHTTPRequest", "JSONKit (= 1.4)"],
"COCOAPODS" => Pod::VERSION
}
should_xcodebuild(podfile.target_definitions[:ios_target])
......@@ -227,7 +236,8 @@ else
pod 'JSONKit', '1.4'
pod 'SSToolkit', '1.0.0'
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
doc = (config.project_pods_root + 'Documentation/JSONKit/html/index.html').read
......@@ -258,7 +268,8 @@ else
end
end
SpecHelper::Installer.new(podfile).install!
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
SpecHelper::Installer.new(resolver).install!
project = Pod::Project.new(config.project_pods_root + 'Pods.xcodeproj')
project.targets.first.build_configurations.map do |config|
config.build_settings['GCC_ENABLE_OBJC_GC']
......@@ -277,10 +288,11 @@ else
pod 'SSZipArchive', '< 2'
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
lock_file_contents = {
lockfile_contents = {
'PODS' => [
# { 'ASIHTTPRequest (1.8.1)' => ["Reachability"] },
# { 'ASIWebPageRequest (1.8.1)' => ["ASIHTTPRequest (= 1.8.1)"] },
......@@ -293,15 +305,16 @@ else
"JSONKit (>= 1.0)",
"Reachability (> 2.0.5)",
"SSZipArchive (< 2)",
]
],
"COCOAPODS" => Pod::VERSION
}
unless platform == :ios
# No Reachability is required by ASIHTTPRequest on OSX
lock_file_contents['DEPENDENCIES'].delete_at(1)
lock_file_contents['PODS'].delete_at(1)
# lock_file_contents['PODS'][0] = 'ASIHTTPRequest (1.8.1)'
lockfile_contents['DEPENDENCIES'].delete_at(1)
lockfile_contents['PODS'].delete_at(1)
# lockfile_contents['PODS'][0] = 'ASIHTTPRequest (1.8.1)'
end
YAML.load(installer.lock_file.read).should == lock_file_contents
installer.lockfile.to_dict.should == lockfile_contents
root = config.project_pods_root
(root + 'Pods.xcconfig').read.should == installer.target_installers.first.xcconfig.to_s
......@@ -320,10 +333,11 @@ else
pod 'Reachability', '2.0.4' # only 2.0.4 is part of ASIHTTPRequest’s source.
end
installer = SpecHelper::Installer.new(spec)
resolver = Pod::Resolver.new(spec, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
YAML.load(installer.lock_file.read).should == {
installer.lockfile.to_dict.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)"],
......@@ -339,7 +353,8 @@ else
pod 'SSZipArchive'
end
installer = SpecHelper::Installer.new(spec)
resolver = Pod::Resolver.new(spec, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
target_definition = installer.target_installers.first.target_definition
installer.specs_by_target[target_definition].first.resources = 'LICEN*', 'Readme.*'
installer.install!
......@@ -356,7 +371,8 @@ else
xcodeproj 'dummy'
pod 'JSONKit'
end
installer = SpecHelper::Installer.new(spec)
resolver = Pod::Resolver.new(spec, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
spec = Pod::Podfile.new do
......@@ -364,7 +380,8 @@ else
xcodeproj 'dummy'
pod 'SSZipArchive'
end
installer = SpecHelper::Installer.new(spec)
resolver = Pod::Resolver.new(spec, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
project = Pod::Project.new(config.project_pods_root + 'Pods.xcodeproj')
......@@ -380,7 +397,9 @@ else
pod 'ASIHTTPRequest'
end
installer = Pod::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(fixture('integration')))
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = Pod::Installer.new(resolver)
installer.install!
project = Xcodeproj::Project.new(config.project_pods_root + 'Pods.xcodeproj')
......@@ -431,7 +450,8 @@ else
pod 'SSZipArchive'
end
installer = SpecHelper::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = SpecHelper::Installer.new(resolver)
installer.install!
workspace = Xcodeproj::Workspace.new_from_xcworkspace(temporary_directory + 'ASIHTTPRequest.xcworkspace')
......@@ -458,7 +478,8 @@ else
pod 'ASIHTTPRequest'
end
installer = Pod::Installer.new(podfile)
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
installer = Pod::Installer.new(resolver)
installer.install!
root = config.project_pods_root
......
......@@ -14,7 +14,10 @@ module Pod
xcodeproj 'MyProject'
pod 'JSONKit'
end
@xcconfig = Installer.new(podfile).target_installers.first.xcconfig.to_hash
sandbox = Sandbox.new(fixture('integration'))
resolver = Resolver.new(podfile, nil, sandbox)
@xcconfig = Installer.new(resolver).target_installers.first.xcconfig.to_hash
end
it "sets the header search paths where installed Pod headers can be found" do
......@@ -35,7 +38,10 @@ module Pod
platform :osx
pod 'ASIHTTPRequest'
end
installer = Installer.new(podfile)
sandbox = Sandbox.new(fixture('integration'))
resolver = Resolver.new(podfile, nil, sandbox)
installer = Installer.new(resolver)
pods = installer.specifications.map do |spec|
LocalPod.new(spec, installer.sandbox, podfile.target_definitions[:default].platform)
end
......@@ -51,7 +57,8 @@ module Pod
pod 'JSONKit'
end
end
installer = Installer.new(podfile)
resolver = Resolver.new(podfile, nil, Sandbox.new(fixture('integration')))
installer = Installer.new(resolver)
installer.target_installers.map(&:target_definition).map(&:name).should == [:not_empty]
end
......@@ -61,7 +68,8 @@ module Pod
platform :ios
xcodeproj path, 'App Store' => :release
end
installer = Installer.new(podfile)
resolver = Resolver.new(podfile, nil, Sandbox.new(fixture('integration')))
installer = Installer.new(resolver)
installer.project.build_configurations.map(&:name).sort.should == ['App Store', 'Debug', 'Release', 'Test']
end
......@@ -70,7 +78,8 @@ module Pod
platform :ios
pod 'JSONKit', :head
end
installer = Installer.new(podfile)
resolver = Resolver.new(podfile, nil, Sandbox.new(fixture('integration')))
installer = Installer.new(resolver)
pod = installer.pods.first
downloader = stub('Downloader')
......
require File.expand_path('../../spec_helper', __FILE__)
describe "Pod::Lockfile" do
extend SpecHelper::TemporaryDirectory
def sample
text = <<-LOCKFILE.strip_heredoc
---
PODS:
- BananaLib (1.0):
- monkey (< 1.0.9, ~> 1.0.1)
- monkey (1.0.8)
DEPENDENCIES:
- BananaLib (~> 1.0)
COCOAPODS: #{Pod::VERSION}
LOCKFILE
end
def podfile
Pod::Podfile.new do
platform :ios
pod 'BananaLib', '~>1.0'
end
end
def specs
specs = [
Pod::Specification.from_file(fixture('banana-lib/BananaLib.podspec')),
Pod::Specification.new do |s|
s.name = "monkey"
s.version = "1.0.8"
end
]
specs.each { |s| s.activate_platform(:ios) }
specs
end
def tmp_path
temporary_directory + 'Podfile.lock'
end
it "loads from a file" 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_yaml.should == sample
end
before do
@lockfile = Pod::Lockfile.create(tmp_path, podfile, specs)
end
it "generates a valid YAML representation" do
@lockfile.to_yaml.should == sample
end
it "generates a valid Dictionary representation" do
@lockfile.to_dict.should == YAML.load(sample)
end
it "returns the Podfile dependencies" do
@lockfile.podfile_dependencies.should == [
Pod::Dependency.new("BananaLib", "~> 1.0")
]
end
it "returns the dependencies for the installed pods" do
@lockfile.installed_dependencies.should == [
Pod::Dependency.new("BananaLib", "= 1.0"),
Pod::Dependency.new("monkey", "= 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.to_dict.should == nil
end
it "serializes correctly `:head' dependencies" do
podfile = Pod::Podfile.new do
platform :ios
pod 'BananaLib', :head
end
specs = [
Pod::Specification.new do |s|
s.name = "BananaLib"
s.version = "1.0"
end,
Pod::Specification.new do |s|
s.name = "monkey"
s.version = "1.0.8"
end
]
specs.each { |s| s.activate_platform(:ios) }
lockfile = Pod::Lockfile.create(tmp_path, podfile, specs)
lockfile.to_dict["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"
end
specs = [
Pod::Specification.new do |s|
s.name = "BananaLib"
s.version = "1.0"
end,
Pod::Specification.new do |s|
s.name = "monkey"
s.version = "1.0.8"
end
]
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')"
end
xit "reads `:heads' dependencies correctly" do
end
xit "reads external dependencies dependencies correctly" do
end
end
......@@ -10,7 +10,7 @@ module Pod
pod 'BlocksKit'
# pod 'ASIWebPageRequest'
end
@resolver = Resolver.new(@podfile, stub('sandbox'))
@resolver = Resolver.new(@podfile, nil, stub('sandbox'))
end
it "holds the context state, such as cached specification sets" do
......@@ -73,7 +73,7 @@ module Pod
pod 'RestKit/Network'
pod 'RestKit/ObjectMapping/XML'
end
resolver = Resolver.new(@podfile, stub('sandbox'))
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{
FileMD5Hash
ISO8601DateFormatter
......@@ -93,7 +93,7 @@ module Pod
platform :ios
pod 'RestKit'
end
resolver = Resolver.new(@podfile, stub('sandbox'))
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{
FileMD5Hash
ISO8601DateFormatter
......@@ -139,7 +139,7 @@ module Pod
end
end
end
resolver = Resolver.new(@podfile, stub('sandbox'))
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
specs = resolver.resolve.values.flatten.map(&:name).sort
specs.should.not.include 'RestKit/ObjectMapping/XML'
specs.should == %w{
......@@ -175,7 +175,7 @@ module Pod
end
end
@podfile.dependencies.first.external_source.stubs(:specification_from_sandbox).returns(spec)
resolver = Resolver.new(@podfile, stub('sandbox'))
resolver = Resolver.new(@podfile, nil, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{ MainSpec/FirstSubSpec MainSpec/FirstSubSpec/SecondSubSpec }
end
......@@ -185,10 +185,48 @@ module Pod
pod 'FileMD5Hash'
pod 'JSONKit', :head
end
resolver = Resolver.new(podfile, stub('sandbox'))
resolver = Resolver.new(podfile, nil, stub('sandbox'))
filemd5hash, jsonkit = resolver.resolve.values.first.sort_by(&:name)
filemd5hash.version.should.not.be.head
jsonkit.version.should.be.head
end
xit "raises if it finds two conflicting dependencies" do
end
describe "Concerning the Lockfile" do
xit "accepts a nil lockfile" do
lambda { Resolver.new(@podfile, nil, stub('sandbox'))}.should.not.raise
end
xit "detects the pods that need to be installed" do
end
xit "detects the pods that don't need to be installed" do
end
xit "detects the pods that can be updated" do
end
xit "doesn't install new pods in `update_mode'" do
end
xit "handles correctly pods with external source" do
end
xit "it always suggest to update pods in head mode" do
end
xit "it prevents a pod from upgrading during an install" do
end
end
end
end
......@@ -14,23 +14,23 @@ describe "Pod::Specification::Set" do
end
it "checks if the dependency of the specification is compatible with existing requirements" do
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '1.2' })
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '< 1.2.1' })
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '> 1.1' })
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '~> 1.2.0' })
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack' })
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '1.2'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.2.1'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '> 1.1'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '~> 1.2.0'), 'Spec')
@set.required_by(Pod::Dependency.new('CocoaLumberjack'), 'Spec')
lambda {
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '< 1.0' })
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.0' ), 'Spec')
}.should.raise Pod::Informative
end
it "raises if the required version doesn't exist" do
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '< 1.0' })
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.0'), 'Spec')
lambda { @set.required_version }.should.raise Pod::Informative
end
before do
@set.required_by(Pod::Spec.new { |s| s.dependency 'CocoaLumberjack', '< 1.2.1' })
@set.required_by(Pod::Dependency.new('CocoaLumberjack', '< 1.2.1'), 'Spec')
end
it "returns the version required for the dependency" do
......@@ -49,4 +49,9 @@ describe "Pod::Specification::Set" do
`touch #{fixture('spec-repos/master/CocoaLumberjack/.DS_Store')}`
lambda { @set.versions }.should.not.raise
end
it "raises if a version is incompatible with the activated version" do
spec = Pod::Dependency.new('CocoaLumberjack', '1.2.1')
lambda { @set.required_by(spec, 'Spec') }.should.raise Pod::Informative
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