Commit 11bf22ff authored by Eloy Duran's avatar Eloy Duran

Merge branch 'subspecs'

parents ce53feaf aef331b2
platform :ios
dependency 'RestKit-ObjectMapping'
dependency 'RestKit-JSON-JSONKit'
dependency 'RestKit/ObjectMapping/JSONKit'
......@@ -3,19 +3,17 @@ PODS:
- LibComponentLogging-Core (1.1.4)
- LibComponentLogging-NSLog (1.0.2):
- LibComponentLogging-Core (>= 1.1.4)
- RestKit-JSON-JSONKit (0.9.3):
- JSONKit
- RestKit (= 0.9.3)
- RestKit-Network (0.9.3):
- RestKit (0.9.3)
- RestKit/Network (0.9.3):
- LibComponentLogging-NSLog
- RestKit (= 0.9.3)
- RestKit-ObjectMapping (0.9.3):
- RestKit/ObjectMapping (0.9.3):
- RestKit (= 0.9.3)
- RestKit-Network (= 0.9.3)
DOWNLOAD_ONLY:
- RestKit (0.9.3)
- RestKit/Network
- RestKit/ObjectMapping/JSONKit (0.9.3):
- JSONKit
- RestKit (= 0.9.3)
- RestKit/ObjectMapping (= 0.9.3)
DEPENDENCIES:
- RestKit-JSON-JSONKit
- RestKit-ObjectMapping
- RestKit/ObjectMapping/JSONKit
......@@ -36,6 +36,26 @@ module Pod
(@specification ? @specification == other.specification : @external_spec_source == other.external_spec_source)
end
def subspec_dependency?
@name.include?('/')
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.
def top_level_spec_name
subspec_dependency? ? @name.split('/').first : @name
end
# Returns a copy of the dependency, but with the name of the top level
# spec. This is used by Pod::Specification::Set to merge dependencies on
# the complete set, irrespective of what spec in the set wil be used.
def to_top_level_spec_dependency
dep = dup
dep.name = top_level_spec_name
dep
end
def to_s
version = ''
if source = @external_spec_source
......
module Pod
class Installer
module Shared
def dependent_specification_sets
@dependent_specification_sets ||= Resolver.new(@podfile, @definition ? @definition.dependencies : nil).resolve
def dependent_specifications
@dependent_specifications ||= Resolver.new(@podfile, @definition ? @definition.dependencies : nil).resolve
end
def build_specifications
dependent_specification_sets.reject(&:only_part_of_other_pod?).map(&:specification)
dependent_specifications.reject do |spec|
spec.wrapper? || spec.defined_in_set.only_part_of_other_pod?
end
end
def download_only_specifications
dependent_specification_sets.select(&:only_part_of_other_pod?).map(&:specification)
dependent_specifications - build_specifications
end
end
......
......@@ -222,8 +222,8 @@ module Pod
@target_definitions.values.map(&:target_dependencies).flatten
end
def dependency_by_name(name)
dependencies.find { |d| d.name == name }
def dependency_by_top_level_spec_name(name)
dependencies.find { |d| d.top_level_spec_name == name }
end
def generate_bridge_support?
......
......@@ -5,19 +5,31 @@ module Pod
end
def resolve
@sets = []
@sets, @loaded_spec_names, @specs = [], [], []
find_dependency_sets(@specification, @dependencies)
@sets
@specs
end
def find_dependency_sets(specification, dependencies = nil)
(dependencies || specification.dependencies).each do |dependency|
set = find_dependency_set(dependency)
set.required_by(specification)
unless @sets.include?(set)
validate_platform!(set)
@sets << set
find_dependency_sets(set.specification)
unless @loaded_spec_names.include?(dependency.name)
# Get a reference to the spec that’s actually being loaded.
# If it’s a subspec dependency, e.g. 'RestKit/Network', then
# find that subspec.
spec = set.specification
if dependency.subspec_dependency?
spec = spec.subspec_by_name(dependency.name)
end
validate_platform!(spec)
# Ensure we don't resolve the same spec twice
@loaded_spec_names << spec.name
@specs << spec
@sets << set unless @sets.include?(set)
find_dependency_sets(spec)
end
end
end
......@@ -30,8 +42,7 @@ module Pod
end
end
def validate_platform!(set)
spec = set.specification
def validate_platform!(spec)
unless spec.platform.nil? || spec.platform == @specification.platform
raise Informative, "The platform required by the Podfile (:#{@specification.platform}) " \
"does not match that of #{spec} (:#{spec.platform})"
......
......@@ -41,7 +41,13 @@ module Pod
end
def search(dependency)
pod_sets.find { |set| set.name == dependency.name }
pod_sets.find do |set|
# First match the (top level) name, which does not yet load the spec from disk
set.name == dependency.top_level_spec_name &&
# Now either check if it's a dependency on the top level spec, or if it's not
# check if the requested subspec exists in the top level spec.
(!dependency.subspec_dependency? || !set.specification.subspec_by_name(dependency.name).nil?)
end
end
def search_by_name(query, full_text_search)
......
......@@ -21,11 +21,16 @@ module Pod
attr_accessor :defined_in_file
def initialize
@dependencies, @resources, @clean_paths = [], [], []
@xcconfig = Xcodeproj::Config.new
post_initialize
yield self if block_given?
end
# TODO This is just to work around a MacRuby bug
def post_initialize
@dependencies, @source_files, @resources, @clean_paths, @subspecs = [], [], [], [], []
@xcconfig = Xcodeproj::Config.new
end
# Attributes
attr_accessor :name
......@@ -53,10 +58,13 @@ module Pod
def summary=(summary)
@summary = summary
@description ||= summary
end
attr_reader :summary
def description
@description || summary
end
def part_of=(*name_and_version_requirements)
self.part_of_dependency = *name_and_version_requirements
@part_of.only_part_of_other_pod = true
......@@ -111,19 +119,14 @@ module Pod
attr_writer :compiler_flags
def compiler_flags
flags = "#{@compiler_flags} "
flags << '-fobjc-arc' if @requires_arc
flags << '-fobjc-arc' if requires_arc
flags
end
# These are attributes which are also on a Podfile
attr_accessor :platform
attr_accessor :requires_arc
attr_accessor :generate_bridge_support
alias_method :generate_bridge_support?, :generate_bridge_support
def dependency(*name_and_version_requirements)
name, *version_requirements = name_and_version_requirements.flatten
dep = Dependency.new(name, *version_requirements)
......@@ -132,18 +135,46 @@ module Pod
end
attr_reader :dependencies
def subspec(name, &block)
subspec = Subspec.new(self, name, &block)
@subspecs << subspec
subspec
end
attr_reader :subspecs
# Not attributes
# TODO when we move to use a 'ResolveContext' this should happen there.
attr_accessor :defined_in_set
include Config::Mixin
def wrapper?
source_files.empty? && !subspecs.empty?
end
def subspec_by_name(name)
# Remove this spec's name from the beginning of the name we’re looking for
# and take the first component from the remainder, which is the spec we need
# to find now.
remainder = name[self.name.size+1..-1].split('/')
subspec_name = remainder.shift
subspec = subspecs.find { |s| s.name == "#{self.name}/#{subspec_name}" }
# If this was the last component in the name, then return the subspec,
# otherwise we recursively keep calling subspec_by_name until we reach the
# last one and return that
remainder.empty? ? subspec : subspec.subspec_by_name(name)
end
def ==(other)
self.class === other &&
object_id == other.object_id ||
(self.class === other &&
name && name == other.name &&
version && version == other.version
version && version == other.version)
end
def dependency_by_name(name)
@dependencies.find { |d| d.name == name }
def dependency_by_top_level_spec_name(name)
@dependencies.find { |d| d.top_level_spec_name == name }
end
def part_of_specification_set
......@@ -284,7 +315,9 @@ module Pod
missing << "`homepage'" unless homepage
missing << "`author(s)'" unless authors
missing << "either `source' or `part_of'" unless source || part_of
missing << "`source_files'" unless source_files
missing << "`source_files'" if source_files.empty? && subspecs.empty?
# TODO
# * validate subspecs
incorrect = []
allowed = [nil, :ios, :osx]
......@@ -363,6 +396,52 @@ module Pod
def post_install(target)
end
class Subspec < Specification
attr_reader :parent
def initialize(parent, name)
@parent, @name = parent, name
# TODO a MacRuby bug, the correct super impl `initialize' is not called consistently
#super(&block)
post_initialize
# A subspec is _always_ part of the source of its top level spec.
self.part_of = top_level_parent.name, version
# A subspec has a dependency on the parent if the parent is a subspec too.
dependency(@parent.name, version) if @parent.is_a?(Subspec)
yield self if block_given?
end
undef_method :name=, :version=, :source=, :defined_in_set=
def top_level_parent
top_level_parent = @parent
top_level_parent = top_level_parent.parent while top_level_parent.is_a?(Subspec)
top_level_parent
end
def name
"#{@parent.name}/#{@name}"
end
# TODO manually forwarding the attributes that we have so far needed to forward,
# but need to think if there's a better way to do this.
def summary
@summary ? @summary : top_level_parent.summary
end
# 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|
define_method(attr) { top_level_parent.send(attr) }
end
def copy_header_mapping(from)
top_level_parent.copy_header_mapping(from)
end
end
end
Spec = Specification
......
......@@ -25,7 +25,7 @@ module Pod
end
def required_by(specification)
dependency = specification.dependency_by_name(name)
dependency = specification.dependency_by_top_level_spec_name(name)
unless @required_by.empty? || dependency.requirement.satisfied_by?(required_version)
# TODO add graph that shows which dependencies led to this.
raise Informative, "#{specification} tries to activate `#{dependency}', " \
......@@ -38,12 +38,12 @@ module Pod
def dependency
@required_by.inject(Dependency.new(name)) do |previous, spec|
previous.merge(spec.dependency_by_name(name))
previous.merge(spec.dependency_by_top_level_spec_name(name).to_top_level_spec_dependency)
end
end
def only_part_of_other_pod?
@required_by.all? { |spec| spec.dependency_by_name(name).only_part_of_other_pod? }
@required_by.all? { |spec| spec.dependency_by_top_level_spec_name(name).only_part_of_other_pod? }
end
def name
......@@ -55,7 +55,7 @@ module Pod
end
def specification
@specification ||= Specification.from_file(specification_path)
@specification ||= Specification.from_file(specification_path).tap { |spec| spec.defined_in_set = self }
end
# Return the first version that matches the current dependency.
......@@ -85,6 +85,7 @@ module Pod
class External < Set
def initialize(specification)
@specification = specification
@specification.defined_in_set = self
@required_by = []
end
......
Subproject commit 49cb88232dc6547bfc9751521a38889ec9f9f803
Subproject commit 8b2cd6ae4b7c3ec41fe6adba6fa3b7b1f34aaadd
......@@ -78,8 +78,8 @@ describe "Pod::Command" do
it "searches for a pod with name, summary, or description matching the given query ignoring case" do
[
['systemCONfiguration', %w{ Reachability }],
['is', %w{ ASIHTTPRequest Reachability SSZipArchive }],
['dROP', %w{ Reachability }],
['is', %w{ ASIHTTPRequest SSZipArchive }],
].each do |query, results|
command = Pod::Command.parse('search', '--silent', '--full', query)
def command.puts(msg = '')
......
......@@ -16,6 +16,20 @@ describe "Pod::Dependency" do
dep1.should == dep2
end
it "returns the name of the dependency, or the name of the pod of which this is a subspec" do
dep = Pod::Dependency.new('RestKit')
dep.top_level_spec_name.should == 'RestKit'
dep = Pod::Dependency.new('RestKit/Networking')
dep.top_level_spec_name.should == 'RestKit'
end
it "returns a copy of the dependency but for the top level spec, if it's a subspec" do
dep = Pod::Dependency.new('RestKit', '>= 1.2.3')
dep.to_top_level_spec_dependency.should == Pod::Dependency.new('RestKit', '>= 1.2.3')
dep = Pod::Dependency.new('RestKit/Networking', '>= 1.2.3')
dep.to_top_level_spec_dependency.should == Pod::Dependency.new('RestKit', '>= 1.2.3')
end
it "is equal to another dependency if `external_spec_source' is the same" do
dep1 = Pod::Dependency.new('bananas', :git => 'GIT-URL')
dep2 = Pod::Dependency.new('bananas')
......
......@@ -14,15 +14,15 @@ describe "Pod::Podfile" do
it "adds dependencies" do
podfile = Pod::Podfile.new { dependency 'ASIHTTPRequest'; dependency 'SSZipArchive', '>= 0.1' }
podfile.dependencies.size.should == 2
podfile.dependency_by_name('ASIHTTPRequest').should == Pod::Dependency.new('ASIHTTPRequest')
podfile.dependency_by_name('SSZipArchive').should == Pod::Dependency.new('SSZipArchive', '>= 0.1')
podfile.dependency_by_top_level_spec_name('ASIHTTPRequest').should == Pod::Dependency.new('ASIHTTPRequest')
podfile.dependency_by_top_level_spec_name('SSZipArchive').should == Pod::Dependency.new('SSZipArchive', '>= 0.1')
end
it "adds a dependency on a Pod repo outside of a spec repo (the repo is expected to contain a podspec)" do
podfile = Pod::Podfile.new do
dependency 'SomeExternalPod', :git => 'GIT-URL', :commit => '1234'
end
dep = podfile.dependency_by_name('SomeExternalPod')
dep = podfile.dependency_by_top_level_spec_name('SomeExternalPod')
dep.external_spec_source.should == { :git => 'GIT-URL', :commit => '1234' }
end
......@@ -30,7 +30,7 @@ describe "Pod::Podfile" do
podfile = Pod::Podfile.new do
dependency 'SomeExternalPod', :podspec => 'http://gist/SomeExternalPod.podspec'
end
dep = podfile.dependency_by_name('SomeExternalPod')
dep = podfile.dependency_by_top_level_spec_name('SomeExternalPod')
dep.external_spec_source.should == { :podspec => 'http://gist/SomeExternalPod.podspec' }
end
......@@ -40,7 +40,7 @@ describe "Pod::Podfile" do
s.name = 'SomeExternalPod'
end
end
dep = podfile.dependency_by_name('SomeExternalPod')
dep = podfile.dependency_by_top_level_spec_name('SomeExternalPod')
dep.specification.name.should == 'SomeExternalPod'
end
......
......@@ -38,13 +38,10 @@ describe "Pod::Resolver" do
Pod::Config.instance = @config_before
end
it "returns all sets needed for the dependency" do
sets = []
sets << Pod::Spec::Set.by_pod_dir(fixture('spec-repos/master/Reachability'))
sets << Pod::Spec::Set.by_pod_dir(fixture('spec-repos/master/ASIHTTPRequest'))
sets << Pod::Spec::Set.by_pod_dir(fixture('spec-repos/master/ASIWebPageRequest'))
resolver = Pod::Resolver.new(@podfile)
resolver.resolve.sort_by(&:name).should == sets.sort_by(&:name)
it "returns all specs needed for the dependency" do
specs = Pod::Resolver.new(@podfile).resolve
specs.map(&:class).uniq.should == [Pod::Specification]
specs.map(&:name).sort.should == %w{ ASIHTTPRequest ASIWebPageRequest Reachability }
end
it "does not raise if all dependencies match the platform of the root spec (Podfile)" do
......@@ -71,5 +68,16 @@ describe "Pod::Resolver" do
resolver.stub_platform = :ios
lambda { resolver.resolve }.should.raise Pod::Informative
end
it "resolves subspecs" do
@podfile = Pod::Podfile.new do
platform :ios
dependency 'RestKit/Network'
dependency 'RestKit/ObjectMapping'
end
config.rootspec = @podfile
resolver = Pod::Resolver.new(@podfile)
resolver.resolve.map(&:name).sort.should == %w{ LibComponentLogging-Core LibComponentLogging-NSLog RestKit RestKit/Network RestKit/ObjectMapping }
end
end
......@@ -20,9 +20,20 @@ describe "Pod::Source" do
set.should == Pod::Spec::Set.by_pod_dir(config.repos_dir + 'repo2/JSONKit')
end
it "returns a specification set by top level spec name" do
set = Pod::Source.search(Pod::Dependency.new('RestKit/Network'))
set.should == Pod::Spec::Set.by_pod_dir(config.repos_dir + 'repo1/RestKit')
end
it "raises if a specification set can't be found" do
lambda {
Pod::Source.search(Pod::Dependency.new('DoesNotExist'))
}.should.raise Pod::Informative
end
it "raises if a subspec can't be found" do
lambda {
Pod::Source.search(Pod::Dependency.new('RestKit/DoesNotExist'))
}.should.raise Pod::Informative
end
end
......@@ -2,6 +2,7 @@ require File.expand_path('../../spec_helper', __FILE__)
describe "A Pod::Specification loaded from a podspec" do
before do
fixture('banana-lib') # ensure the archive is unpacked
@spec = Pod::Specification.from_file(fixture('banana-lib/BananaLib.podspec'))
end
......@@ -58,7 +59,7 @@ describe "A Pod::Specification loaded from a podspec" do
it "returns the pod's dependencies" do
expected = Pod::Dependency.new('monkey', '~> 1.0.1', '< 1.0.9')
@spec.dependencies.should == [expected]
@spec.dependency_by_name('monkey').should == expected
@spec.dependency_by_top_level_spec_name('monkey').should == expected
end
it "returns the pod's xcconfig settings" do
......@@ -279,3 +280,64 @@ describe "A Pod::Specification, in general," do
list.glob.should == FileList[(ROOT + '*').to_s].exclude('Rakefile').map { |path| Pathname.new(path) }
end
end
describe "A Pod::Specification subspec" do
before do
@spec = Pod::Spec.new do |s|
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'
fss.subspec 'SecondSubSpec' do |sss|
end
end
end
end
it "makes a parent spec a wrapper if it has no source files of its own" do
@spec.should.be.wrapper
@spec.subspecs.first.should.not.be.wrapper
end
it "returns the top level parent spec" do
@spec.subspecs.first.top_level_parent.should == @spec
@spec.subspecs.first.subspecs.first.top_level_parent.should == @spec
end
it "is named after the parent spec" do
@spec.subspecs.first.name.should == 'MainSpec/FirstSubSpec'
@spec.subspecs.first.subspecs.first.name.should == 'MainSpec/FirstSubSpec/SecondSubSpec'
end
it "is a `part_of' the top level parent spec" do
dependency = Pod::Dependency.new('MainSpec', '1.2.3').tap { |d| d.only_part_of_other_pod = true }
@spec.subspecs.first.part_of.should == dependency
@spec.subspecs.first.subspecs.first.part_of.should == dependency
end
it "depends on the parent spec, if it is a subspec" do
dependency = Pod::Dependency.new('MainSpec', '1.2.3').tap { |d| d.only_part_of_other_pod = true }
@spec.subspecs.first.dependencies.should == [dependency]
@spec.subspecs.first.subspecs.first.dependencies.should == [dependency, Pod::Dependency.new('MainSpec/FirstSubSpec', '1.2.3')]
end
it "automatically forwards undefined attributes to the top level parent" do
[:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags].each do |attr|
@spec.subspecs.first.send(attr).should == @spec.send(attr)
@spec.subspecs.first.subspecs.first.send(attr).should == @spec.send(attr)
end
end
it "returns subspecs by name" do
@spec.subspec_by_name('MainSpec/FirstSubSpec').should == @spec.subspecs.first
@spec.subspec_by_name('MainSpec/FirstSubSpec/SecondSubSpec').should == @spec.subspecs.first.subspecs.first
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