Commit 5190db62 authored by Fabio Pelosin's avatar Fabio Pelosin

[Resolver] Clean up.

parent ee5eb989
......@@ -6,11 +6,12 @@
###### TODO
- Add Rake FileList warning.
- Rake FileList is not working.
- Enable CocoaPods Core-warnings
- Bad bug with Specification default values being corroded exponentially in subsequent calls.
- Dropped script for resources.
- Added support for `prefix_header_file` in subspecs
- Added support for `prefix_header_contents` in subspecs
- LocalPod needs to be updated for some changes done to the DSL
###### Specification DSL
......@@ -29,14 +30,14 @@
###### Enhancements
- Released preview [documentation](docs.cocoapods.org).
- CocoaPods now has support for working in teams and not committing the Pods folder.
- Released [documentation](docs.cocoapods.org).
- CocoaPods now can infer the platform from the integrated targets.
- Adds new subcommand `pod spec cat NAME` to print a spec file to standard output.
- Added Podfile to the Pods project.
- The `--no-clean` option of the `pod spec lint` command now displays the Pods project for inspection.
- CocoaPods now can infer the platform from the integrated targets.
- It is now possible to specify default values for the configuration in `~/.cocoapods/config.yaml`.
- CocoaPods now keeps track of the checksum of the specifications of the installed Pods and reinstalls them if needed.
- It is now possible to specify default values for the configuration in `~/.cocoapods/config.yaml` ([example]()).
- CocoaPods now checks the checksums of the installed specifications and reinstalls them if needed.
###### Bug fixes
......
GIT
remote: git://github.com/CocoaPods/Core.git
revision: 2acbcad47f48373f515b5c0c5d383d96fed21152
revision: 08a308017a4fbf327143f14353cf698460088ac9
specs:
cocoapods-core (0.17.0.alpha)
activesupport (~> 3.2.6)
......
......@@ -3,10 +3,17 @@ module Pod
# The resolver is responsible of generating a list of specifications grouped
# by target for a given Podfile.
#
# Its current implementation is naive, in the sense that it can't do full
# @todo Its current implementation is naive, in the sense that it can't do full
# automatic resolves like Bundler:
# [how-does-bundler-bundle](http://patshaughnessy.net/2011/9/24/how-does-bundler-bundle)
#
# @todo Another limitation is that the order of the dependencies matter. The
# current implementation could create issues, for example, if a
# specification is loaded for a target definition and later for another
# target is set in head mode. The first specification will not be in head
# mode.
#
#
class Resolver
include Config::Mixin
......@@ -33,9 +40,10 @@ module Pod
@sandbox = sandbox
@podfile = podfile
@locked_dependencies = locked_dependencies
@allow_pre_downloads = true
end
# @return [Bool] whether the resolver should update the external specs
# @return [Bool] Whether the resolver should update the external specs
# in the resolution process. This option is used for detecting
# changes in with the Podfile without affecting the existing Pods
# installation
......@@ -43,17 +51,24 @@ module Pod
# @note This option is used by `pod outdated`.
#
attr_accessor :update_external_specs
alias_method :update_external_specs?, :update_external_specs
# @todo Implement non destructive resolution.
# @return [Bool] Whether pre-downloads should be allowed. Pre-downloads
# change the state of the sandbox and should be allowed only during
# installations. Defaults to true.
#
# @note Enabling this if the Podfile and the sandbox are not in sync
# might result in an exception.
#
attr_accessor :allow_pre_downloads
alias_method :allow_pre_downloads?, :allow_pre_downloads
#-------------------------------------------------------------------------#
# @!group Resolution
public
# @!group Resolution
# Identifies the specifications that should be installed.
#
# @return [Hash{TargetDefinition => Array<Specification>}] specs_by_target
......@@ -65,7 +80,6 @@ module Pod
@cached_sets = {}
@cached_specs = {}
@specs_by_target = {}
# @pods_from_external_sources = []
podfile.target_definitions.values.each do |target|
UI.section "Resolving dependencies for target `#{target.name}' (#{target.platform})" do
......@@ -87,57 +101,48 @@ module Pod
#
attr_reader :specs_by_target
# @return [Array<Specification>] All the specifications resolved.
#
def specs
specs_by_target.values.flatten.uniq
end
# @return [Array<Strings>] The name of the pods that have an
# external source.
#
# TODO: Not sure if needed.
#
# attr_reader :pods_from_external_sources
#-------------------------------------------------------------------------#
# !@ Resolution context
private
# !@ Resolution context
# @return [Source::Aggregate] A cache of the sources needed to find the
# podspecs.
#
# @note The sources are cached because frequently accessed by the
# resolver and loading them requires disk activity.
#
attr_accessor :cached_sources
# @return [Hash<String => Set>] A cache that keeps tracks of the sets
# loaded by the resolution process.
#
# @note Sets keep track of the TODO:
# @note Sets store the resolved dependencies and return the highest
# available specification found in the sources. This is done
# globally and not per target definition because there can be just
# one Pod installation, so different version of the same Pods for
# target definitions are not allowed.
#
attr_accessor :cached_sets
#
# @return [Hash<String => Specification>] The loaded specifications grouped
# by name.
#
attr_accessor :cached_specs
#
#
attr_writer :specs_by_target
#-------------------------------------------------------------------------#
# !@ Resolution helpers
private
# !@ Helpers
# Resolves recursively the dependencies of a specification and stores them
# in the @cached_specs ivar.
#
# @param [Podfile, Specification] dependent_spec
# the specification whose dependencies are being resolved.
# @param [Podfile, Specification, #to_s] dependent_spec
# the specification whose dependencies are being resolved. Used
# only for UI purposes.
#
# @param [Array<Dependency>] dependencies
# the dependencies of the specification.
......@@ -148,19 +153,16 @@ module Pod
# @note If there is a locked dependency with the same name of a
# given dependency the locked one is used in place of the
# dependency of the specification. In this way it is possible to
# not updated the installed pods without without introducing
# dependencies in other target definitions.
#
# @todo Just add the requirement to the set?
# @todo Use root name?
# prevent the update of the version of installed pods not changed
# in the Podfile.
#
# @note The recursive process checks if a dependency has already been
# loaded to prevent an infinite loop. For this reason the
# @loaded_specs ivar must be cleaned when changing target
# definition.
# loaded to prevent an infinite loop.
#
#
# @todo The set class should be aware whether it is in head mode.
# @note The set class merges all (of all the target definitions) the
# dependencies and thus it keeps track of whether it is in head
# mode or from an external source because {Dependency#merge}
# preserves this information.
#
# @return [void]
#
......@@ -177,7 +179,6 @@ module Pod
spec = set.specification.subspec_by_name(dependency.name)
@loaded_specs << spec.name
cached_specs[spec.name] = spec
# @pods_from_external_sources << spec.root_name if dependency.external?
validate_platform(spec, target_definition)
spec.activate_platform(target_definition.platform)
spec.version.head = dependency.head?
......@@ -198,18 +199,12 @@ module Pod
# the remote. Otherwise the specification is retrieved from the
# sandbox that fetches the external source only if needed.
#
# @todo If the set is loaded from a normal source and then from an
# external one that information is lost.
# @todo Check dependency.specification
#
# @return [Set] the cached set for a given dependency.
#
def find_cached_set(dependency)
name = dependency.root_name
unless cached_sets[name]
if dependency.specification
set = Specification::Set::External.new(dependency.specification)
elsif dependency.external_source
if dependency.external_source
set = set_from_external_source(dependency)
else
set = cached_sources.search(dependency)
......@@ -221,20 +216,34 @@ module Pod
# Returns a new set created from an external source
#
# @param [Dependency] dependency
# The dependency with the external source for which the set is
# needed.
#
# @return [Set] the set for the dependency.
#
def set_from_external_source(dependency)
source = ExternalSources.from_dependency(dependency)
if update_external_specs
if allow_pre_downloads?
if update_external_specs?
spec = source.specification_from_external(sandbox)
else
spec = source.specification(sandbox)
end
else
spec = sandbox.specification(dependency.name)
unless spec
raise Informative, "Unable to find the specification for " \
"`#{dependency}`. Running `pod install` should fix the issue."
end
end
set = Specification::Set::External.new(spec)
set
end
# Ensures that a spec is compatible with the platform of a target.
# Ensures that a specification is compatible with the platform of a target.
#
# @raise If the spec is not supported by the target.
# @raise If the specification is not supported by the target.
#
# @return [void]
#
......
require File.expand_path('../../spec_helper', __FILE__)
require File.expand_path('../../../spec_helper', __FILE__)
# @return [Analyzer] the sample analyzer.
#
......
......@@ -4,7 +4,6 @@ module Pod
describe Resolver do
describe "In general" do
before do
config.repos_dir = fixture('spec-repos')
@podfile = Podfile.new do
platform :ios
pod 'BlocksKit', '1.5.2'
......@@ -26,11 +25,19 @@ module Pod
end
it "allows to specify whether the external sources should be updated against the remote" do
# TODO
@resolver.update_external_specs = true
@resolver.update_external_specs.should.be.true
end
it "allows to specify whether the sandbox can be modified and pre-downloads are allowed" do
@resolver.allow_pre_downloads = false
@resolver.allow_pre_downloads.should.be.false
end
it "allows pre-downloads by default" do
@resolver.allow_pre_downloads.should.be.true
end
#--------------------------------------#
it "resolves the specification of the podfile" do
......@@ -54,16 +61,6 @@ module Pod
]
end
it "returns all the resolved specifications" do
@resolver.resolve
@resolver.specs.map(&:class).uniq.should == [Specification]
@resolver.specs.map(&:to_s).should == [
"A2DynamicDelegate (2.0.2)",
"BlocksKit (1.5.2)",
"libffi (3.0.11)"
]
end
it "it resolves specifications from external sources" do
podspec = fixture('integration/Reachability/Reachability.podspec')
podfile = Podfile.new do
......@@ -72,7 +69,8 @@ module Pod
end
resolver = Resolver.new(config.sandbox, podfile)
resolver.resolve
resolver.specs.map(&:to_s).should == ['Reachability (3.0.0)']
specs = resolver.specs_by_target.values.flatten
specs.map(&:to_s).should == ['Reachability (3.0.0)']
end
end
......@@ -80,7 +78,6 @@ module Pod
describe "Resolution" do
before do
config.repos_dir = fixture('spec-repos')
@podfile = Podfile.new do
platform :ios, '6.0'
pod 'BlocksKit', '1.5.2'
......@@ -88,6 +85,44 @@ module Pod
@resolver = Resolver.new(config.sandbox, @podfile)
end
it "cross resolves dependencies" do
@podfile = Podfile.new do
platform :ios, '6.0'
pod 'BlocksKit', '= 1.0'
pod 'libffi', '< 3.0'
end
dependant_1_0 = Spec.new do |s|
s.name = 'BlocksKit'
s.version = '1.0'
s.platform = :ios
s.dependency 'libffi', '1.0'
end
depended_1_0 = Spec.new do |s|
s.name = 'libffi'
s.version = '1.0'
s.platform = :ios
end
depended_2_0 = Spec.new do |s|
s.name = 'libffi'
s.version = '2.0'
s.platform = :ios
end
Source.any_instance.stubs(:versions).with('BlocksKit').returns([Version.new(1.0)])
Source.any_instance.stubs(:specification).with('BlocksKit', Version.new('1.0')).returns(dependant_1_0)
Source.any_instance.stubs(:versions).with('libffi').returns([Version.new(1.0), Version.new(2.0)])
Source.any_instance.stubs(:specification).with('libffi', Version.new('1.0')).returns(depended_1_0)
Source.any_instance.stubs(:specification).with('libffi', Version.new('2.0')).returns(depended_2_0)
resolver = Resolver.new(config.sandbox, @podfile)
specs = resolver.resolve.values.flatten.map(&:to_s).sort
specs.should == ["BlocksKit (1.0)", "libffi (1.0)"]
end
it "holds the context state, such as cached specification sets" do
@resolver.resolve
cached_sets = @resolver.send(:cached_sets)
......@@ -132,7 +167,7 @@ module Pod
}
end
it "resolves subspecs with external constraints" do
it "handles correctly subspecs from external sources" do
@podfile = Podfile.new do
platform :ios
pod 'MainSpec/FirstSubSpec', :git => 'GIT-URL'
......@@ -141,11 +176,6 @@ module Pod
s.name = 'MainSpec'
s.version = '1.2.3'
s.platform = :ios
s.license = 'MIT'
s.author = 'Joe the Plumber'
s.summary = 'A spec with subspecs'
s.source = { :git => '/some/url' }
s.requires_arc = true
s.subspec 'FirstSubSpec' do |fss|
fss.source_files = 'some/file'
......@@ -154,10 +184,11 @@ module Pod
end
ExternalSources::GitSource.any_instance.stubs(:specification).returns(spec)
resolver = Resolver.new(config.sandbox, @podfile)
resolver.resolve.values.flatten.map(&:name).sort.should == %w{ MainSpec/FirstSubSpec MainSpec/FirstSubSpec/SecondSubSpec }
specs = resolver.resolve.values.flatten.map(&:name).sort
specs.should == %w{ MainSpec/FirstSubSpec MainSpec/FirstSubSpec/SecondSubSpec }
end
it "marks a specification's version to be a `bleeding edge' version" do
it "marks a specification's version to be a HEAD version" do
podfile = Podfile.new do
platform :ios
pod 'FileMD5Hash'
......@@ -180,260 +211,52 @@ module Pod
e.message.should.match(/already activated version/)
end
xit "is robust against infinite loops" do
it "takes into account locked dependencies" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', "<= 1.5pre"
end
resolver = Resolver.new(config.sandbox, podfile)
version = resolver.resolve.values.flatten.first.version
version.to_s.should == '1.5pre'
locked_deps = [Dependency.new('JSONKit', "= 1.4")]
resolver = Resolver.new(config.sandbox, podfile, locked_deps)
version = resolver.resolve.values.flatten.first.version
version.to_s.should == '1.4'
end
end
#-------------------------------------------------------------------------#
describe "#set_from_external_source" do
before do
@resolver = Resolver.new(config.sandbox, nil)
end
xit "takes into account locked dependencies" do
it "it fetches the specification from either the sandbox or from the remote be default" do
dependency = Dependency.new('Name', :git => 'www.example.com')
ExternalSources::GitSource.any_instance.expects(:specification_from_external).returns(Specification.new).once
@resolver.send(:set_from_external_source, dependency)
end
it "it fetches the specification from the remote if in update mode" do
dependency = Dependency.new('Name', :git => 'www.example.com')
ExternalSources::GitSource.any_instance.expects(:specification).returns(Specification.new).once
@resolver.update_external_specs = false
@resolver.send(:set_from_external_source, dependency)
end
xit "transfers the head state of a dependency to a specification" do
end
xit "" do
end
xit "" do
end
xit "" do
end
# describe "Concerning Installation mode" do
# before do
# config.repos_dir = fixture('spec-repos')
# @podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit', '1.5.2'
# pod 'JSONKit'
# end
# @specs = [
# Specification.new do |s|
# s.name = "BlocksKit"
# s.version = "1.5.2"
# end,
# Specification.new do |s|
# s.name = "JSONKit"
# s.version = "1.4"
# end ]
# @specs.each { |s| s.activate_platform(:ios) }
# @resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
# end
# 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 update the version of pods still compatible with the Podfile" do
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "JSONKit (1.4)"
# end
# 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', '1.5.2'
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "BlocksKit (1.5.2)"
# installed.should.include? "JSONKit (1.5pre)"
# end
# it "installs pods added to the Podfile" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit'
# pod 'BlocksKit'
# pod 'libPusher', '1.3' # New pod
# 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
# 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
# ExternalSources::GitSource.any_instance.stubs(:specification_from_sandbox).returns(spec)
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# @resolver.should_install?("JSONKit").should.be.false
# end
# it "doesn't updates the repos if there no change in the pods" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit'
# end
# config.skip_repo_update = false
# Command::Repo.any_instance.expects(:run).never
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# it "updates the repos if there is a new pod" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit'
# pod 'libPusher' # New pod
# end
# config.skip_repo_update = false
# Command::Repo::Update.any_instance.expects(:run).once
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# it "doesn't update the repos if config indicate to skip it in any case" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit', :head #changed to head
# pod 'libPusher' # New pod
# end
# config.skip_repo_update = true
# Command::Repo::Update.any_instance.expects(:run).never
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# it "updates the repos if there is a new pod" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit'
# pod 'JSONKit', :head #changed to head
# end
# config.skip_repo_update = false
# Command::Repo::Update.any_instance.expects(:run).once
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.resolve
# end
# end
# describe "Concerning Update mode" do
# before do
# config.repos_dir = fixture('spec-repos')
# previous_podfile = Podfile.new do
# platform :ios
# pod 'JSONKit'
# pod 'libPusher'
# end
# @specs = [
# Specification.new do |s|
# s.name = "libPusher"
# s.version = "1.3"
# end,
# Specification.new do |s|
# s.name = "JSONKit"
# s.version = "1.4"
# end ]
# @specs.each { |s| s.activate_platform(:ios) }
# @lockfile = Lockfile.generate(previous_podfile, @specs)
# @podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit', '1.5.2'
# pod 'JSONKit'
# pod 'libPusher'
# end
# @resolver = Resolver.new(@podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# end
# it "identifies the pods that can be updated" do
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.should.include? "JSONKit (999.999.999)"
# @resolver.should_install?("JSONKit").should.be.true
# end
# it "respects the constraints of the podfile" do
# podfile = Podfile.new do
# platform :ios
# pod 'BlocksKit', '1.5.2'
# 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
# it "installs new pods" do
# installed = @resolver.resolve.values.flatten.map(&:to_s)
# installed.join(' ').should.include?('BlocksKit')
# @resolver.should_install?("BlocksKit").should.be.true
# 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
# it "always updates the repos even if there is change in the pods" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit'
# pod 'libPusher'
# end
# config.skip_repo_update = false
# Command::Repo::Update.any_instance.expects(:run).once
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# @resolver.resolve
# end
# # TODO: stub the specification resolution for the sandbox
# xit "it always suggests to update pods from external sources" do
# podfile = Podfile.new do
# platform :ios
# pod 'libPusher', :git => "example.com"
# end
# @resolver = Resolver.new(podfile, @lockfile, stub('sandbox'))
# @resolver.update_mode = true
# @resolver.resolve
# @resolver.should_install?("libPusher").should.be.true
# end
# end
it "it fetches the specification only from the sandbox if pre-downloads are disabled" do
dependency = Dependency.new('Name', :git => 'www.example.com')
Sandbox.any_instance.expects(:specification).returns(Specification.new).once
@resolver.allow_pre_downloads = true
@resolver.send(:set_from_external_source, dependency)
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