Commit 8c2adafa authored by Eloy Duran's avatar Eloy Duran

No longer cache Specification::Set in the Set class, but in a Context.

parent 9765ffa0
...@@ -3,14 +3,14 @@ module Pod ...@@ -3,14 +3,14 @@ module Pod
autoload :TargetInstaller, 'cocoapods/installer/target_installer' autoload :TargetInstaller, 'cocoapods/installer/target_installer'
include Config::Mixin include Config::Mixin
attr_reader :sandbox attr_reader :sandbox
def initialize(podfile) def initialize(podfile)
@podfile = podfile @podfile = podfile
# FIXME: pass this into the installer as a parameter # FIXME: pass this into the installer as a parameter
@sandbox = Sandbox.new(config.project_pods_root) @sandbox = Sandbox.new(config.project_pods_root)
@resolver = Resolver.new(@podfile, @sandbox)
end end
def lock_file def lock_file
...@@ -22,8 +22,8 @@ module Pod ...@@ -22,8 +22,8 @@ module Pod
@project = Pod::Project.for_platform(@podfile.platform) @project = Pod::Project.for_platform(@podfile.platform)
# First we need to resolve dependencies across *all* targets, so that the # First we need to resolve dependencies across *all* targets, so that the
# same correct versions of pods are being used for all targets. This # same correct versions of pods are being used for all targets. This
# happens when we call `build_specifications'. # happens when we call `activated_specifications'.
build_specifications.each do |spec| activated_specifications.each do |spec|
# Add all source files to the project grouped by pod # Add all source files to the project grouped by pod
group = @project.add_pod_group(spec.name) group = @project.add_pod_group(spec.name)
spec.expanded_source_files.each do |path| spec.expanded_source_files.each do |path|
...@@ -42,7 +42,7 @@ module Pod ...@@ -42,7 +42,7 @@ module Pod
end end
def install_dependencies! def install_dependencies!
build_specifications.map do |spec| activated_specifications.map do |spec|
LocalPod.new(spec, sandbox).tap do |pod| LocalPod.new(spec, sandbox).tap do |pod|
if pod.exists? || spec.local? if pod.exists? || spec.local?
puts "Using #{pod}" unless config.silent? puts "Using #{pod}" unless config.silent?
...@@ -63,17 +63,17 @@ module Pod ...@@ -63,17 +63,17 @@ module Pod
def install! def install!
@sandbox.prepare_for_install @sandbox.prepare_for_install
puts "Installing dependencies of: #{@podfile.defined_in_file}" if config.verbose? puts "Installing dependencies of: #{@podfile.defined_in_file}" if config.verbose?
pods = install_dependencies! pods = install_dependencies!
puts "Generating support files" unless config.silent? puts "Generating support files" unless config.silent?
target_installers.each do |target_installer| target_installers.each do |target_installer|
target_specs = build_specifications_for_target(target_installer.target_definition) target_specs = activated_specifications_for_target(target_installer.target_definition)
pods_for_target = pods.select { |pod| target_specs.include?(pod.specification) } pods_for_target = pods.select { |pod| target_specs.include?(pod.specification) }
target_installer.install!(pods_for_target, sandbox) target_installer.install!(pods_for_target, sandbox)
end end
generate_lock_file!(pods) generate_lock_file!(pods)
puts "* Running post install hooks" if config.verbose? puts "* Running post install hooks" if config.verbose?
...@@ -83,17 +83,16 @@ module Pod ...@@ -83,17 +83,16 @@ module Pod
puts "* Writing Xcode project file to `#{@sandbox.project_path}'" if config.verbose? puts "* Writing Xcode project file to `#{@sandbox.project_path}'" if config.verbose?
project.save_as(@sandbox.project_path) project.save_as(@sandbox.project_path)
end end
def run_post_install_hooks def run_post_install_hooks
# we loop over target installers instead of pods, because we yield the target installer # we loop over target installers instead of pods, because we yield the target installer
# to the spec post install hook. # to the spec post install hook.
target_installers.each do |target_installer| target_installers.each do |target_installer|
build_specifications_for_target(target_installer.target_definition).each do |spec| activated_specifications_for_target(target_installer.target_definition).each do |spec|
spec.post_install(target_installer) spec.post_install(target_installer)
end end
end end
@podfile.post_install!(self) @podfile.post_install!(self)
end end
...@@ -126,27 +125,29 @@ module Pod ...@@ -126,27 +125,29 @@ module Pod
end end
end end
end end
def dependent_specifications_for_each_target_definition def dependent_specifications_for_each_target_definition
@dependent_specifications_for_each_target_definition ||= Resolver.new(@podfile, @sandbox).resolve @dependent_specifications_for_each_target_definition ||= @resolver.resolve
end end
def dependent_specifications def dependent_specifications
dependent_specifications_for_each_target_definition.values.flatten dependent_specifications_for_each_target_definition.values.flatten
end end
def build_specifications def activated_specifications
dependent_specifications.reject do |spec| dependent_specifications.reject do |spec|
spec.wrapper? || spec.defined_in_set.only_part_of_other_pod? # Don't activate specs which are only wrappers of subspecs, or share
# source with another pod but aren't activated themselves.
spec.wrapper? || @resolver.context.sets[spec.name].only_part_of_other_pod?
end end
end end
def build_specifications_for_target(target_definition) def activated_specifications_for_target(target_definition)
dependent_specifications_for_each_target_definition[target_definition] dependent_specifications_for_each_target_definition[target_definition]
end end
def download_only_specifications def download_only_specifications
dependent_specifications - build_specifications dependent_specifications - activated_specifications
end end
end end
end end
...@@ -54,7 +54,7 @@ module Pod ...@@ -54,7 +54,7 @@ module Pod
def prefix_header_filename def prefix_header_filename
"#{@target_definition.lib_name}-prefix.pch" "#{@target_definition.lib_name}-prefix.pch"
end end
def target_support_files def target_support_files
[copy_resources_filename, prefix_header_filename, xcconfig_filename] [copy_resources_filename, prefix_header_filename, xcconfig_filename]
end end
...@@ -82,7 +82,7 @@ module Pod ...@@ -82,7 +82,7 @@ module Pod
configure_build_configurations(xcconfig_file) configure_build_configurations(xcconfig_file)
create_files(pods, sandbox) create_files(pods, sandbox)
end end
def configure_build_configurations(xcconfig_file) def configure_build_configurations(xcconfig_file)
@target.build_configurations.each do |config| @target.build_configurations.each do |config|
config.base_configuration = xcconfig_file config.base_configuration = xcconfig_file
......
module Pod module Pod
class Resolver class Resolver
# A Resolver::Context caches specification sets and is used by the resolver
# to ensure that extra dependencies on a set are added to the same instance.
#
# In addition, the context is later on used by Specification to lookup other
# specs, like the on they are a part of.
class Context
attr_reader :sources, :sets, :sandbox
def initialize(sandbox)
@sandbox = sandbox
@sets = {}
@sources = Source::Aggregate.new
end
def find_dependency_set(dependency)
@sets[dependency.name] ||= begin
if dependency.specification
Specification::Set::External.new(dependency.specification)
elsif external_source = dependency.external_source
specification = external_source.specification_from_sandbox(@sandbox)
Specification::Set::External.new(specification)
else
@sources.search(dependency)
end
end
end
end
attr_reader :podfile, :sandbox
attr_accessor :context
def initialize(podfile, sandbox) def initialize(podfile, sandbox)
@podfile = podfile @podfile = podfile
@sandbox = sandbox @sandbox = sandbox
@context = Context.new(@sandbox)
end end
def resolve def resolve
@podfile.target_definitions.values.inject({}) do |result, target_definition| @specs = {}
@sets, @loaded_spec_names, @specs = [], [], []
result = @podfile.target_definitions.values.inject({}) do |result, target_definition|
@loaded_specs = []
find_dependency_sets(@podfile, target_definition.dependencies) find_dependency_sets(@podfile, target_definition.dependencies)
result[target_definition] = @specs.sort_by(&:name) result[target_definition] = @specs.values_at(*@loaded_specs).sort_by(&:name)
result result
end end
# Specification doesn't need to know more about a context, so we assign
# the other specification, of which this pod is a part, to the spec.
@specs.values.sort_by(&:name).each do |spec|
if spec.part_of_other_pod?
spec.part_of_specification = @context.sets[spec.part_of.name].specification
end
end
result
end end
private
# this can be called with anything that has dependencies # this can be called with anything that has dependencies
# e.g. a Specification or a Podfile. # e.g. a Specification or a Podfile.
def find_dependency_sets(has_dependencies, dependency_subset = nil) def find_dependency_sets(specification, dependencies = nil)
(dependency_subset || has_dependencies.dependencies).each do |dependency| (dependencies || specification.dependencies).each do |dependency|
set = find_dependency_set(dependency) set = @context.find_dependency_set(dependency)
set.required_by(has_dependencies) set.required_by(specification)
unless @loaded_spec_names.include?(dependency.name) unless @loaded_specs.include?(dependency.name)
# Get a reference to the spec that’s actually being loaded. # Get a reference to the spec that’s actually being loaded.
# If it’s a subspec dependency, e.g. 'RestKit/Network', then # If it’s a subspec dependency, e.g. 'RestKit/Network', then
# find that subspec. # find that subspec.
...@@ -28,29 +74,18 @@ module Pod ...@@ -28,29 +74,18 @@ module Pod
if dependency.subspec_dependency? if dependency.subspec_dependency?
spec = spec.subspec_by_name(dependency.name) spec = spec.subspec_by_name(dependency.name)
end end
validate_platform!(spec) validate_platform!(spec)
# Ensure we don't resolve the same spec twice @loaded_specs << spec.name
@loaded_spec_names << spec.name @specs[spec.name] = spec
@specs << spec
@sets << set unless @sets.include?(set)
# And recursively load the dependencies of the spec.
find_dependency_sets(spec) find_dependency_sets(spec)
end end
end end
end end
def find_dependency_set(dependency)
if dependency.specification
Specification::Set::External.new(dependency.specification)
elsif external_source = dependency.external_source
specification = external_source.specification_from_sandbox(@sandbox)
Specification::Set::External.new(specification)
else
Source.search(dependency)
end
end
def validate_platform!(spec) def validate_platform!(spec)
unless spec.platform.nil? || spec.platform == @podfile.platform unless spec.platform.nil? || spec.platform == @podfile.platform
raise Informative, "The platform required by the Podfile (:#{@podfile.platform}) " \ raise Informative, "The platform required by the Podfile (:#{@podfile.platform}) " \
......
module Pod module Pod
class Source class Source
def self.all class Aggregate
@sources ||= begin def all
repos_dir = Config.instance.repos_dir @sources ||= begin
unless repos_dir.exist? repos_dir = Config.instance.repos_dir
raise Informative, "No spec repos found in `#{repos_dir}'. " \ unless repos_dir.exist?
"To fetch the `master' repo run: $ pod setup" raise Informative, "No spec repos found in `#{repos_dir}'. " \
"To fetch the `master' repo run: $ pod setup"
end
repos_dir.children.select(&:directory?).map { |repo| Source.new(repo) }
end
end
def search(dependency)
all.map { |s| s.search(dependency) }.compact.first ||
raise(Informative, "Unable to find a pod named `#{dependency.name}'")
end
def search_by_name(query, full_text_search)
result = all.map { |s| s.search_by_name(query, full_text_search) }.flatten
if result.empty?
extra = ", summary, or description" if full_text_search
raise(Informative, "Unable to find a pod with name" \
"#{extra} matching `#{query}'")
end end
repos_dir.children.select(&:directory?).map { |repo| new(repo) } result
end end
end end
def self.all
Aggregate.new.all
end
def self.search(dependency) def self.search(dependency)
all.map { |s| s.search(dependency) }.compact.first || Aggregate.new.search(dependency)
raise(Informative, "Unable to find a pod named `#{dependency.name}'")
end end
def self.search_by_name(query, full_text_search) def self.search_by_name(name, full_text_search)
result = all.map { |s| s.search_by_name(query, full_text_search) }.flatten Aggregate.new.search_by_name(name, full_text_search)
if result.empty?
extra = ", summary, or description" if full_text_search
raise(Informative, "Unable to find a pod with name" \
"#{extra} matching `#{query}'")
end
result
end end
attr_reader :repo attr_reader :repo
...@@ -35,7 +49,7 @@ module Pod ...@@ -35,7 +49,7 @@ module Pod
def pod_sets def pod_sets
@repo.children.map do |child| @repo.children.map do |child|
if child.directory? && child.basename.to_s != '.git' if child.directory? && child.basename.to_s != '.git'
Specification::Set.by_pod_dir(child) Specification::Set.new(child)
end end
end.compact end.compact
end end
......
...@@ -150,9 +150,6 @@ module Pod ...@@ -150,9 +150,6 @@ module Pod
# Not attributes # Not attributes
# TODO when we move to use a 'ResolveContext' this should happen there.
attr_accessor :defined_in_set
include Config::Mixin include Config::Mixin
def local? def local?
...@@ -163,6 +160,10 @@ module Pod ...@@ -163,6 +160,10 @@ module Pod
Pathname.new(File.expand_path(source[:local])) Pathname.new(File.expand_path(source[:local]))
end end
# This is assigned the other spec, of which this pod's source is a part, by
# a Resolver.
attr_accessor :part_of_specification
def wrapper? def wrapper?
source_files.empty? && !subspecs.empty? source_files.empty? && !subspecs.empty?
end end
...@@ -191,17 +192,6 @@ module Pod ...@@ -191,17 +192,6 @@ module Pod
@dependencies.find { |d| d.top_level_spec_name == name } @dependencies.find { |d| d.top_level_spec_name == name }
end end
def part_of_specification_set
if part_of
Set.by_specification_name(part_of.name)
end
end
# Returns the specification for the pod that this pod's source is a part of.
def part_of_specification
(set = part_of_specification_set) && set.specification
end
def pod_destroot def pod_destroot
if part_of_other_pod? if part_of_other_pod?
part_of_specification.pod_destroot part_of_specification.pod_destroot
...@@ -368,7 +358,7 @@ module Pod ...@@ -368,7 +358,7 @@ module Pod
yield self if block_given? yield self if block_given?
end end
undef_method :name=, :version=, :source=, :defined_in_set= undef_method :name=, :version=, :source=
def top_level_parent def top_level_parent
top_level_parent = @parent top_level_parent = @parent
...@@ -388,7 +378,7 @@ module Pod ...@@ -388,7 +378,7 @@ module Pod
end end
# Override the getters to always return the value of the top level parent spec. # Override the getters to always return the value of the top level parent spec.
[:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags, :defined_in_set].each do |attr| [:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags].each do |attr|
define_method(attr) { top_level_parent.send(attr) } define_method(attr) { top_level_parent.send(attr) }
end end
......
module Pod module Pod
class Specification class Specification
class Set class Set
def self.sets
@sets ||= {}
end
def self.by_specification_name(name)
sets[name]
end
# This keeps an identity map of sets so that you always get the same Set
# instance for the same pod directory.
def self.by_pod_dir(pod_dir)
set = new(pod_dir)
sets[set.name] ||= set
sets[set.name]
end
attr_reader :pod_dir attr_reader :pod_dir
def initialize(pod_dir) def initialize(pod_dir)
...@@ -56,7 +40,7 @@ module Pod ...@@ -56,7 +40,7 @@ module Pod
end end
def specification def specification
@specification ||= Specification.from_file(specification_path).tap { |spec| spec.defined_in_set = self } @specification ||= Specification.from_file(specification_path)
end end
# Return the first version that matches the current dependency. # Return the first version that matches the current dependency.
...@@ -86,7 +70,6 @@ module Pod ...@@ -86,7 +70,6 @@ module Pod
class External < Set class External < Set
def initialize(specification) def initialize(specification)
@specification = specification @specification = specification
@specification.defined_in_set = self
@required_by = [] @required_by = []
end end
......
...@@ -244,7 +244,7 @@ else ...@@ -244,7 +244,7 @@ else
installer = SpecHelper::Installer.new(spec) installer = SpecHelper::Installer.new(spec)
target_definition = installer.target_installers.first.target_definition target_definition = installer.target_installers.first.target_definition
installer.build_specifications_for_target(target_definition).first.resources = 'LICEN*', 'Readme.*' installer.activated_specifications_for_target(target_definition).first.resources = 'LICEN*', 'Readme.*'
installer.install! installer.install!
contents = (config.project_pods_root + 'Pods-resources.sh').read contents = (config.project_pods_root + 'Pods-resources.sh').read
......
require File.expand_path('../../spec_helper', __FILE__) require File.expand_path('../../spec_helper', __FILE__)
describe "Pod::Installer" do describe "Pod::Installer" do
before do
@config_before = config
Pod::Config.instance = nil
config.silent = true
config.repos_dir = fixture('spec-repos')
config.project_pods_root = fixture('integration')
end
after do
Pod::Config.instance = @config_before
end
describe ", by default," do describe ", by default," do
before do before do
@xcconfig = Pod::Installer.new(Pod::Podfile.new do @xcconfig = Pod::Installer.new(Pod::Podfile.new do
...@@ -19,18 +31,6 @@ describe "Pod::Installer" do ...@@ -19,18 +31,6 @@ describe "Pod::Installer" do
end end
end end
before do
@config_before = config
Pod::Config.instance = nil
config.silent = true
config.repos_dir = fixture('spec-repos')
config.project_pods_root = fixture('integration')
end
after do
Pod::Config.instance = @config_before
end
it "generates a BridgeSupport metadata file from all the pod headers" do it "generates a BridgeSupport metadata file from all the pod headers" do
podfile = Pod::Podfile.new do podfile = Pod::Podfile.new do
platform :osx platform :osx
...@@ -39,7 +39,7 @@ describe "Pod::Installer" do ...@@ -39,7 +39,7 @@ describe "Pod::Installer" do
config.rootspec = podfile config.rootspec = podfile
expected = [] expected = []
installer = Pod::Installer.new(podfile) installer = Pod::Installer.new(podfile)
pods = installer.build_specifications.map do |spec| pods = installer.activated_specifications.map do |spec|
spec.header_files.each do |header| spec.header_files.each do |header|
expected << config.project_pods_root + header expected << config.project_pods_root + header
end end
......
...@@ -10,7 +10,7 @@ class StubbedSet < Pod::Specification::Set ...@@ -10,7 +10,7 @@ class StubbedSet < Pod::Specification::Set
end end
end end
class StubbedResolver < Pod::Resolver class StubbedContext < Pod::Resolver::Context
attr_accessor :stub_platform attr_accessor :stub_platform
def find_dependency_set(dependency) def find_dependency_set(dependency)
...@@ -38,6 +38,16 @@ describe "Pod::Resolver" do ...@@ -38,6 +38,16 @@ describe "Pod::Resolver" do
Pod::Config.instance = @config_before Pod::Config.instance = @config_before
end end
it "has a ResolveContext which holds global state, such as cached specification sets" do
resolver = Pod::Resolver.new(@podfile, stub('sandbox'))
resolver.resolve
resolver.context.sets.values.sort_by(&:name).should == [
Pod::Spec::Set.new(config.repos_dir + 'master/ASIHTTPRequest'),
Pod::Spec::Set.new(config.repos_dir + 'master/ASIWebPageRequest'),
Pod::Spec::Set.new(config.repos_dir + 'master/Reachability'),
].sort_by(&:name)
end
it "returns all specs needed for the dependency" do it "returns all specs needed for the dependency" do
specs = Pod::Resolver.new(@podfile, stub('sandbox')).resolve.values.flatten specs = Pod::Resolver.new(@podfile, stub('sandbox')).resolve.values.flatten
specs.map(&:class).uniq.should == [Pod::Specification] specs.map(&:class).uniq.should == [Pod::Specification]
...@@ -54,18 +64,19 @@ describe "Pod::Resolver" do ...@@ -54,18 +64,19 @@ describe "Pod::Resolver" do
end end
it "raises once any of the dependencies does not match the platform of the root spec (Podfile)" do it "raises once any of the dependencies does not match the platform of the root spec (Podfile)" do
resolver = StubbedResolver.new(config.rootspec, stub('sandbox')) resolver = Pod::Resolver.new(config.rootspec, stub('sandbox'))
context = resolver.context = StubbedContext.new(resolver.sandbox)
@podfile.platform :ios @podfile.platform :ios
resolver.stub_platform = :ios context.stub_platform = :ios
lambda { resolver.resolve }.should.not.raise lambda { resolver.resolve }.should.not.raise
resolver.stub_platform = :osx context.stub_platform = :osx
lambda { resolver.resolve }.should.raise Pod::Informative lambda { resolver.resolve }.should.raise Pod::Informative
@podfile.platform :osx @podfile.platform :osx
resolver.stub_platform = :osx context.stub_platform = :osx
lambda { resolver.resolve }.should.not.raise lambda { resolver.resolve }.should.not.raise
resolver.stub_platform = :ios context.stub_platform = :ios
lambda { resolver.resolve }.should.raise Pod::Informative lambda { resolver.resolve }.should.raise Pod::Informative
end end
...@@ -77,7 +88,13 @@ describe "Pod::Resolver" do ...@@ -77,7 +88,13 @@ describe "Pod::Resolver" do
end end
config.rootspec = @podfile config.rootspec = @podfile
resolver = Pod::Resolver.new(@podfile, stub('sandbox')) resolver = Pod::Resolver.new(@podfile, stub('sandbox'))
resolver.resolve.values.flatten.map(&:name).sort.should == %w{ LibComponentLogging-Core LibComponentLogging-NSLog RestKit RestKit/Network RestKit/ObjectMapping } resolver.resolve.values.flatten.map(&:name).sort.should == %w{
LibComponentLogging-Core
LibComponentLogging-NSLog
RestKit
RestKit/Network
RestKit/ObjectMapping
}
end end
end end
...@@ -15,14 +15,14 @@ describe "Pod::Source" do ...@@ -15,14 +15,14 @@ describe "Pod::Source" do
it "returns a specification set by name from any spec repo" do it "returns a specification set by name from any spec repo" do
set = Pod::Source.search(Pod::Dependency.new('Reachability')) set = Pod::Source.search(Pod::Dependency.new('Reachability'))
set.should == Pod::Spec::Set.by_pod_dir(config.repos_dir + 'repo1/Reachability') set.should == Pod::Spec::Set.new(config.repos_dir + 'repo1/Reachability')
set = Pod::Source.search(Pod::Dependency.new('JSONKit')) set = Pod::Source.search(Pod::Dependency.new('JSONKit'))
set.should == Pod::Spec::Set.by_pod_dir(config.repos_dir + 'repo2/JSONKit') set.should == Pod::Spec::Set.new(config.repos_dir + 'repo2/JSONKit')
end end
it "returns a specification set by top level spec name" do it "returns a specification set by top level spec name" do
set = Pod::Source.search(Pod::Dependency.new('RestKit/Network')) set = Pod::Source.search(Pod::Dependency.new('RestKit/Network'))
set.should == Pod::Spec::Set.by_pod_dir(config.repos_dir + 'repo1/RestKit') set.should == Pod::Spec::Set.new(config.repos_dir + 'repo1/RestKit')
end end
it "raises if a specification set can't be found" do it "raises if a specification set can't be found" do
......
...@@ -7,24 +7,11 @@ class Pod::Spec::Set ...@@ -7,24 +7,11 @@ class Pod::Spec::Set
end end
describe "Pod::Specification::Set" do describe "Pod::Specification::Set" do
it "returns nil in case a set hasn't been resolved yet" do
Pod::Spec::Set.reset!
Pod::Spec::Set.by_specification_name('CocoaLumberjack').should == nil
end
before do before do
@set = Pod::Spec::Set.by_pod_dir(fixture('spec-repos/master/CocoaLumberjack')) @set = Pod::Spec::Set.new(fixture('spec-repos/master/CocoaLumberjack'))
@set.reset! @set.reset!
end end
it "returns a cached set by name once it has been resolved once" do
Pod::Spec::Set.by_specification_name('CocoaLumberjack').should.eql @set
end
it "always returns the same set instance for a pod dir" do
Pod::Spec::Set.by_pod_dir(fixture('spec-repos/master/CocoaLumberjack')).should.eql @set
end
it "returns the name of the pod" do it "returns the name of the pod" do
@set.name.should == 'CocoaLumberjack' @set.name.should == 'CocoaLumberjack'
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