cocoapods 0.5.1 → 0.6.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/CHANGELOG.md +229 -2
  2. data/README.md +50 -20
  3. data/bin/pod +3 -2
  4. data/lib/cocoapods.rb +23 -9
  5. data/lib/cocoapods/command.rb +71 -30
  6. data/lib/cocoapods/command/error_report.rb +102 -0
  7. data/lib/cocoapods/command/install.rb +27 -19
  8. data/lib/cocoapods/command/list.rb +51 -8
  9. data/lib/cocoapods/command/presenter.rb +61 -0
  10. data/lib/cocoapods/command/presenter/cocoa_pod.rb +123 -0
  11. data/lib/cocoapods/command/push.rb +102 -0
  12. data/lib/cocoapods/command/repo.rb +70 -14
  13. data/lib/cocoapods/command/search.rb +7 -10
  14. data/lib/cocoapods/command/setup.rb +76 -15
  15. data/lib/cocoapods/command/spec.rb +581 -97
  16. data/lib/cocoapods/config.rb +23 -26
  17. data/lib/cocoapods/dependency.rb +86 -40
  18. data/lib/cocoapods/downloader.rb +30 -18
  19. data/lib/cocoapods/downloader/git.rb +125 -15
  20. data/lib/cocoapods/downloader/http.rb +73 -0
  21. data/lib/cocoapods/downloader/mercurial.rb +3 -9
  22. data/lib/cocoapods/downloader/subversion.rb +3 -9
  23. data/lib/cocoapods/executable.rb +26 -3
  24. data/lib/cocoapods/generator/acknowledgements.rb +37 -0
  25. data/lib/cocoapods/generator/acknowledgements/markdown.rb +38 -0
  26. data/lib/cocoapods/generator/acknowledgements/plist.rb +63 -0
  27. data/lib/cocoapods/generator/copy_resources_script.rb +8 -4
  28. data/lib/cocoapods/generator/documentation.rb +99 -0
  29. data/lib/cocoapods/generator/dummy_source.rb +14 -0
  30. data/lib/cocoapods/installer.rb +140 -109
  31. data/lib/cocoapods/installer/target_installer.rb +78 -83
  32. data/lib/cocoapods/installer/user_project_integrator.rb +162 -0
  33. data/lib/cocoapods/local_pod.rb +240 -0
  34. data/lib/cocoapods/platform.rb +41 -18
  35. data/lib/cocoapods/podfile.rb +234 -21
  36. data/lib/cocoapods/project.rb +67 -0
  37. data/lib/cocoapods/resolver.rb +62 -32
  38. data/lib/cocoapods/sandbox.rb +63 -0
  39. data/lib/cocoapods/source.rb +42 -20
  40. data/lib/cocoapods/specification.rb +294 -271
  41. data/lib/cocoapods/specification/set.rb +10 -28
  42. data/lib/cocoapods/specification/statistics.rb +112 -0
  43. metadata +124 -11
  44. data/lib/cocoapods/xcodeproj_pods.rb +0 -111
@@ -0,0 +1,63 @@
1
+ require 'fileutils'
2
+
3
+ module Pod
4
+ class Sandbox
5
+ attr_reader :root
6
+
7
+ HEADERS_DIR = "Headers"
8
+
9
+ def initialize(path)
10
+ @root = Pathname.new(path)
11
+ @header_search_paths = [HEADERS_DIR]
12
+
13
+ FileUtils.mkdir_p(@root)
14
+ end
15
+
16
+ def implode
17
+ root.rmtree
18
+ end
19
+
20
+ def headers_root
21
+ root + HEADERS_DIR
22
+ end
23
+
24
+ def project_path
25
+ root + "Pods.xcodeproj"
26
+ end
27
+
28
+ def add_header_file(namespace_path, relative_header_path)
29
+ namespaced_header_path = headers_root + namespace_path
30
+ namespaced_header_path.mkpath unless File.exist?(namespaced_header_path)
31
+ source = (root + relative_header_path).relative_path_from(namespaced_header_path)
32
+ Dir.chdir(namespaced_header_path) { FileUtils.ln_sf(source, relative_header_path.basename)}
33
+ @header_search_paths << namespaced_header_path.relative_path_from(root)
34
+ namespaced_header_path + relative_header_path.basename
35
+ end
36
+
37
+ def add_header_files(namespace_path, relative_header_paths)
38
+ relative_header_paths.map { |path| add_header_file(namespace_path, path) }
39
+ end
40
+
41
+ def header_search_paths
42
+ @header_search_paths.uniq.map { |path| "${PODS_ROOT}/#{path}" }
43
+ end
44
+
45
+ def prepare_for_install
46
+ headers_root.rmtree if headers_root.exist?
47
+ end
48
+
49
+ def podspec_for_name(name)
50
+ if spec_path = Dir[root + "#{name}/*.podspec"].first
51
+ Pathname.new(spec_path)
52
+ elsif spec_path = Dir[root + "Local Podspecs/#{name}.podspec"].first
53
+ Pathname.new(spec_path)
54
+ end
55
+ end
56
+
57
+ def installed_pod_named(name, platform)
58
+ if spec_path = podspec_for_name(name)
59
+ LocalPod.from_podspec(spec_path, self, platform)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,29 +1,51 @@
1
1
  module Pod
2
2
  class Source
3
- def self.all
4
- @sources ||= begin
5
- repos_dir = Config.instance.repos_dir
6
- unless repos_dir.exist?
7
- raise Informative, "No spec repos found in `#{repos_dir}'. " \
8
- "To fetch the `master' repo run: $ pod setup"
3
+ class Aggregate
4
+ def all
5
+ @sources ||= begin
6
+ repos_dir = Config.instance.repos_dir
7
+ unless repos_dir.exist?
8
+ raise Informative, "No spec repos found in `#{repos_dir}'. " \
9
+ "To fetch the `master' repo run: $ pod setup"
10
+ end
11
+ repos_dir.children.select(&:directory?).map { |repo| Source.new(repo) }
12
+ end
13
+ end
14
+
15
+ def all_sets
16
+ all.map(&:pod_sets).flatten
17
+ end
18
+
19
+ def search(dependency)
20
+ all.map { |s| s.search(dependency) }.compact.first ||
21
+ raise(Informative, "[!] Unable to find a pod named `#{dependency.name}'".red)
22
+ end
23
+
24
+ def search_by_name(query, full_text_search)
25
+ result = all.map { |s| s.search_by_name(query, full_text_search) }.flatten
26
+ if result.empty?
27
+ extra = ", author, summary, or description" if full_text_search
28
+ raise(Informative, "Unable to find a pod with name" \
29
+ "#{extra} matching `#{query}'")
9
30
  end
10
- repos_dir.children.select(&:directory?).map { |repo| new(repo) }
31
+ result
11
32
  end
12
33
  end
13
34
 
35
+ def self.all
36
+ Aggregate.new.all
37
+ end
38
+
39
+ def self.all_sets
40
+ Aggregate.new.all_sets
41
+ end
42
+
14
43
  def self.search(dependency)
15
- all.map { |s| s.search(dependency) }.compact.first ||
16
- raise(Informative, "Unable to find a pod named `#{dependency.name}'")
44
+ Aggregate.new.search(dependency)
17
45
  end
18
46
 
19
- def self.search_by_name(query, full_text_search)
20
- result = all.map { |s| s.search_by_name(query, full_text_search) }.flatten
21
- if result.empty?
22
- extra = ", summary, or description" if full_text_search
23
- raise(Informative, "Unable to find a pod with name" \
24
- "#{extra} matching `#{query}'")
25
- end
26
- result
47
+ def self.search_by_name(name, full_text_search)
48
+ Aggregate.new.search_by_name(name, full_text_search)
27
49
  end
28
50
 
29
51
  attr_reader :repo
@@ -35,7 +57,7 @@ module Pod
35
57
  def pod_sets
36
58
  @repo.children.map do |child|
37
59
  if child.directory? && child.basename.to_s != '.git'
38
- Specification::Set.by_pod_dir(child)
60
+ Specification::Set.new(child)
39
61
  end
40
62
  end.compact
41
63
  end
@@ -46,7 +68,7 @@ module Pod
46
68
  set.name == dependency.top_level_spec_name &&
47
69
  # Now either check if it's a dependency on the top level spec, or if it's not
48
70
  # check if the requested subspec exists in the top level spec.
49
- (!dependency.subspec_dependency? || !set.specification.subspec_by_name(dependency.name).nil?)
71
+ set.specification.subspec_by_name(dependency.name)
50
72
  end
51
73
  end
52
74
 
@@ -54,7 +76,7 @@ module Pod
54
76
  pod_sets.map do |set|
55
77
  text = if full_text_search
56
78
  s = set.specification
57
- "#{s.name} #{s.summary} #{s.description}"
79
+ "#{s.name} #{s.authors} #{s.summary} #{s.description}"
58
80
  else
59
81
  set.name
60
82
  end
@@ -1,3 +1,5 @@
1
+ require 'xcodeproj/config'
2
+
1
3
  module Pod
2
4
  extend Config::Mixin
3
5
 
@@ -6,166 +8,293 @@ module Pod
6
8
  end
7
9
 
8
10
  class Specification
9
- autoload :Set, 'cocoapods/specification/set'
11
+ autoload :Set, 'cocoapods/specification/set'
12
+ autoload :Statistics, 'cocoapods/specification/statistics'
13
+
14
+ ### Initalization
10
15
 
11
16
  # The file is expected to define and return a Pods::Specification.
12
- def self.from_file(path)
17
+ # If name is equals to nil it returns the top level Specification,
18
+ # otherwise it returned the specification with the name that matches
19
+ def self.from_file(path, subspec_name = nil)
13
20
  unless path.exist?
14
21
  raise Informative, "No podspec exists at path `#{path}'."
15
22
  end
16
- spec = Pod._eval_podspec(path)
23
+ spec = ::Pod._eval_podspec(path)
17
24
  spec.defined_in_file = path
18
- spec
25
+ spec.subspec_by_name(subspec_name)
19
26
  end
20
27
 
21
- attr_accessor :defined_in_file
28
+ def initialize(parent = nil, name = nil)
29
+ @parent, @name = parent, name
30
+ @define_for_platforms = [:osx, :ios]
31
+ @clean_paths, @subspecs = [], []
32
+ @deployment_target = {}
33
+ unless parent
34
+ @source = {:git => ''}
35
+ end
22
36
 
23
- def initialize
24
- post_initialize
25
- yield self if block_given?
26
- end
37
+ # multi-platform attributes
38
+ %w[ source_files resources preserve_paths exclude_header_search_paths frameworks libraries dependencies compiler_flags].each do |attr|
39
+ instance_variable_set( "@#{attr}", { :ios => [], :osx => [] } )
40
+ end
41
+ @xcconfig = { :ios => Xcodeproj::Config.new, :osx => Xcodeproj::Config.new }
27
42
 
28
- # TODO This is just to work around a MacRuby bug
29
- def post_initialize
30
- @dependencies, @source_files, @resources, @clean_paths, @subspecs = [], [], [], [], []
31
- @platform = Platform.new(nil)
32
- @xcconfig = Xcodeproj::Config.new
43
+ yield self if block_given?
33
44
  end
34
45
 
35
- # Attributes
36
-
37
- attr_accessor :name
38
- attr_accessor :homepage
39
- attr_accessor :description
40
- attr_accessor :source
41
- attr_accessor :license
46
+ ### Meta programming
42
47
 
43
- attr_reader :version
44
- def version=(version)
45
- @version = Version.new(version)
48
+ # Creates a top level attribute reader. A lambda can
49
+ # be passed to process the ivar before returning it
50
+ def self.top_attr_reader(attr, read_lambda = nil)
51
+ define_method(attr) do
52
+ ivar = instance_variable_get("@#{attr}")
53
+ @parent ? top_level_parent.send(attr) : ( read_lambda ? read_lambda.call(self, ivar) : ivar )
54
+ end
46
55
  end
47
56
 
48
- def authors=(*names_and_email_addresses)
49
- list = names_and_email_addresses.flatten
50
- unless list.first.is_a?(Hash)
51
- authors = list.last.is_a?(Hash) ? list.pop : {}
52
- list.each { |name| authors[name] = nil }
57
+ # Creates a top level attribute writer. A lambda can
58
+ # be passed to initalize the value
59
+ def self.top_attr_writer(attr, init_lambda = nil)
60
+ define_method("#{attr}=") do |value|
61
+ raise Informative, "Can't set `#{attr}' for subspecs." if @parent
62
+ instance_variable_set("@#{attr}", init_lambda ? init_lambda.call(value) : value);
53
63
  end
54
- @authors = authors || list.first
55
64
  end
56
- alias_method :author=, :authors=
57
- attr_reader :authors
58
65
 
66
+ # Creates a top level attribute accessor. A lambda can
67
+ # be passed to initialize the value in the attribute writer.
68
+ def self.top_attr_accessor(attr, writer_labmda = nil)
69
+ top_attr_reader attr
70
+ top_attr_writer attr, writer_labmda
71
+ end
59
72
 
60
- def summary=(summary)
61
- @summary = summary
73
+ # Returns the value of the attribute for the active platform
74
+ # chained with the upstream specifications. The ivar must store
75
+ # the platform specific values as an array.
76
+ def self.pltf_chained_attr_reader(attr)
77
+ define_method(attr) do
78
+ active_plaform_check
79
+ ivar_value = instance_variable_get("@#{attr}")[active_platform]
80
+ @parent ? @parent.send(attr) + ivar_value : ( ivar_value )
81
+ end
62
82
  end
63
- attr_reader :summary
64
83
 
65
- def description
66
- @description || summary
84
+ def active_plaform_check
85
+ raise Informative, "#{self.inspect} not activated for a platform before consumption." unless active_platform
67
86
  end
68
87
 
69
- def part_of=(*name_and_version_requirements)
70
- self.part_of_dependency = *name_and_version_requirements
71
- @part_of.only_part_of_other_pod = true
88
+ # Attribute writer that works in conjuction with the PlatformProxy.
89
+ def self.platform_attr_writer(attr, block = nil)
90
+ define_method("#{attr}=") do |value|
91
+ current = instance_variable_get("@#{attr}")
92
+ @define_for_platforms.each do |platform|
93
+ block ? current[platform] = block.call(value, current[platform]) : current[platform] = value
94
+ end
95
+ end
72
96
  end
73
- attr_reader :part_of
74
97
 
75
- def part_of_dependency=(*name_and_version_requirements)
76
- @part_of = dependency(*name_and_version_requirements)
98
+ def self.pltf_chained_attr_accessor(attr, block = nil)
99
+ pltf_chained_attr_reader(attr)
100
+ platform_attr_writer(attr, block)
77
101
  end
78
102
 
79
- def source_files=(patterns)
80
- @source_files = pattern_list(patterns)
103
+ # The PlatformProxy works in conjuction with Specification#_on_platform.
104
+ # It allows a syntax like `spec.ios.source_files = file`
105
+ class PlatformProxy
106
+ def initialize(specification, platform)
107
+ @specification, @platform = specification, platform
108
+ end
109
+
110
+ %w{ source_files= resource= resources= xcconfig= framework= frameworks= library= libraries= compiler_flags= deployment_target= dependency }.each do |method|
111
+ define_method(method) do |args|
112
+ @specification._on_platform(@platform) do
113
+ @specification.send(method, args)
114
+ end
115
+ end
116
+ end
81
117
  end
82
- attr_reader :source_files
83
118
 
84
- def resources=(patterns)
85
- @resources = pattern_list(patterns)
119
+ def ios
120
+ PlatformProxy.new(self, :ios)
86
121
  end
87
- attr_reader :resources
88
- alias_method :resource=, :resources=
89
122
 
90
- def clean_paths=(patterns)
91
- @clean_paths = pattern_list(patterns)
123
+ def osx
124
+ PlatformProxy.new(self, :osx)
92
125
  end
93
- attr_reader :clean_paths
126
+
127
+ ### Deprecated attributes - TODO: remove once master repo and fixtures have been updated
128
+
129
+ attr_writer :part_of_dependency
130
+ attr_writer :part_of
131
+
132
+ top_attr_accessor :clean_paths, lambda { |patterns| pattern_list(patterns) }
94
133
  alias_method :clean_path=, :clean_paths=
95
134
 
96
- def xcconfig=(hash)
97
- @xcconfig.merge!(hash)
135
+ ### Regular attributes
136
+
137
+ attr_accessor :parent
138
+ attr_accessor :preferred_dependency
139
+
140
+ def name
141
+ @parent ? "#{@parent.name}/#{@name}" : @name
98
142
  end
99
- attr_reader :xcconfig
143
+ attr_writer :name
144
+
145
+ ### Attributes that return the first value defined in the chain
100
146
 
101
- def frameworks=(*frameworks)
102
- frameworks.unshift('')
103
- self.xcconfig = { 'OTHER_LDFLAGS' => frameworks.join(' -framework ').strip }
147
+ def platform
148
+ @platform || ( @parent ? @parent.platform : nil )
104
149
  end
105
- alias_method :framework=, :frameworks=
106
150
 
107
- def libraries=(*libraries)
108
- libraries.unshift('')
109
- self.xcconfig = { 'OTHER_LDFLAGS' => libraries.join(' -l').strip }
151
+ def platform=(platform)
152
+ @platform = Platform.new(*platform)
110
153
  end
111
- alias_method :library=, :libraries=
112
154
 
113
- def header_dir=(dir)
114
- @header_dir = Pathname.new(dir)
155
+ # If not platform is specified all the platforms are returned.
156
+ def available_platforms
157
+ platform.nil? ? @define_for_platforms.map { |platform| Platform.new(platform, deployment_target(platform)) } : [ platform ]
115
158
  end
116
- def header_dir
117
- @header_dir || pod_destroot_name
159
+
160
+ ### Top level attributes. These attributes represent the unique features of pod and can't be specified by subspecs.
161
+
162
+ top_attr_accessor :defined_in_file
163
+ top_attr_accessor :source
164
+ top_attr_accessor :homepage
165
+ top_attr_accessor :summary
166
+ top_attr_accessor :documentation
167
+ top_attr_accessor :requires_arc
168
+ top_attr_accessor :license, lambda { |l| ( l.kind_of? String ) ? { :type => l } : l }
169
+ top_attr_accessor :version, lambda { |v| Version.new(v) }
170
+ top_attr_accessor :authors, lambda { |a| parse_authors(a) }
171
+ top_attr_accessor :header_mappings_dir, lambda { |file| Pathname.new(file) } # If not provided the headers files are flattened
172
+ top_attr_accessor :prefix_header_file, lambda { |file| Pathname.new(file) }
173
+ top_attr_accessor :prefix_header_contents
174
+
175
+ top_attr_reader :description, lambda {|instance, ivar| ivar || instance.summary }
176
+ top_attr_writer :description
177
+
178
+ top_attr_reader :header_dir, lambda {|instance, ivar| ivar || instance.pod_destroot_name }
179
+ top_attr_writer :header_dir, lambda {|dir| Pathname.new(dir) }
180
+
181
+ alias_method :author=, :authors=
182
+
183
+ def self.parse_authors(*names_and_email_addresses)
184
+ list = names_and_email_addresses.flatten
185
+ unless list.first.is_a?(Hash)
186
+ authors = list.last.is_a?(Hash) ? list.pop : {}
187
+ list.each { |name| authors[name] = nil }
188
+ end
189
+ authors || list.first
118
190
  end
119
191
 
120
- attr_writer :compiler_flags
121
- def compiler_flags
122
- flags = "#{@compiler_flags} "
123
- flags << '-fobjc-arc' if requires_arc
124
- flags
192
+ ### Attributes **with** multiple platform support
193
+
194
+ pltf_chained_attr_accessor :source_files, lambda {|value, current| pattern_list(value) }
195
+ pltf_chained_attr_accessor :resources, lambda {|value, current| pattern_list(value) }
196
+ pltf_chained_attr_accessor :preserve_paths, lambda {|value, current| pattern_list(value) } # Paths that should not be cleaned
197
+ pltf_chained_attr_accessor :exclude_header_search_paths, lambda {|value, current| pattern_list(value) } # Headers to be excluded from being added to search paths (RestKit)
198
+ pltf_chained_attr_accessor :frameworks, lambda {|value, current| (current << value).flatten }
199
+ pltf_chained_attr_accessor :libraries, lambda {|value, current| (current << value).flatten }
200
+
201
+ alias_method :resource=, :resources=
202
+ alias_method :framework=, :frameworks=
203
+ alias_method :library=, :libraries=
204
+
205
+ platform_attr_writer :xcconfig, lambda {|value, current| current.tap { |c| c.merge!(value) } }
206
+
207
+ def xcconfig
208
+ raw_xconfig.dup.
209
+ tap { |x| x.libraries.merge libraries }.
210
+ tap { |x| x.frameworks.merge frameworks }
125
211
  end
126
212
 
127
- def platform=(platform)
128
- @platform = Platform.new(platform)
213
+ def raw_xconfig
214
+ @parent ? @parent.raw_xconfig.merge(@xcconfig[active_platform]) : @xcconfig[active_platform]
129
215
  end
130
- attr_reader :platform
131
216
 
132
- attr_accessor :requires_arc
217
+
218
+ def compiler_flags
219
+ if @parent
220
+ chained = @compiler_flags[active_platform].clone
221
+ # TODO hack to get the parent's compiler flags without it being
222
+ # converted to a String by Specification#compiler_flags.
223
+ chained.unshift @parent.instance_variable_get(:@compiler_flags)[active_platform]
224
+ else
225
+ chained = @compiler_flags[active_platform].clone
226
+ chained.unshift '-fobjc-arc' if @requires_arc
227
+ chained.unshift ''
228
+ end
229
+ chained.join(' ')
230
+ end
231
+
232
+ platform_attr_writer :compiler_flags, lambda {|value, current| current << value }
133
233
 
134
234
  def dependency(*name_and_version_requirements)
135
235
  name, *version_requirements = name_and_version_requirements.flatten
236
+ raise Informative, "A specification can't require self as a subspec" if name == self.name
237
+ raise Informative, "A subspec can't require one of its parents specifications" if @parent && @parent.name.include?(name)
136
238
  dep = Dependency.new(name, *version_requirements)
137
- @dependencies << dep
239
+ @define_for_platforms.each do |platform|
240
+ @dependencies[platform] << dep
241
+ end
138
242
  dep
139
243
  end
140
- attr_reader :dependencies
141
244
 
142
- def subspec(name, &block)
143
- subspec = Subspec.new(self, name, &block)
144
- @subspecs << subspec
145
- subspec
245
+ # External dependencies are inherited by subspecs
246
+ def external_dependencies(all_platforms = false)
247
+ active_plaform_check unless all_platforms
248
+ result = all_platforms ? @dependencies.values.flatten : @dependencies[active_platform]
249
+ result += parent.external_dependencies if parent
250
+ result
146
251
  end
147
- attr_reader :subspecs
148
252
 
149
- # Not attributes
253
+ # A specification inherits the preferred_dependency or
254
+ # all the compatible subspecs as dependencies
255
+ def subspec_dependencies
256
+ active_plaform_check
257
+ specs = preferred_dependency ? [subspec_by_name("#{name}/#{preferred_dependency}")] : subspecs
258
+ specs.compact \
259
+ .select { |s| s.supports_platform?(active_platform) } \
260
+ .map { |s| Dependency.new(s.name, version) }
261
+ end
150
262
 
151
- # TODO when we move to use a 'ResolveContext' this should happen there.
152
- attr_accessor :defined_in_set
263
+ def dependencies
264
+ external_dependencies + subspec_dependencies
265
+ end
153
266
 
154
267
  include Config::Mixin
155
-
156
- def local?
157
- !source.nil? && !source[:local].nil?
268
+
269
+ def top_level_parent
270
+ @parent ? @parent.top_level_parent : self
158
271
  end
159
-
160
- def local_path
161
- Pathname.new(File.expand_path(source[:local]))
272
+
273
+ def subspec?
274
+ !@parent.nil?
162
275
  end
163
276
 
164
- def wrapper?
165
- source_files.empty? && !subspecs.empty?
277
+ def subspec(name, &block)
278
+ subspec = Specification.new(self, name, &block)
279
+ @subspecs << subspec
280
+ subspec
281
+ end
282
+ attr_reader :subspecs
283
+
284
+ def recursive_subspecs
285
+ unless @recursive_subspecs
286
+ mapper = lambda do |spec|
287
+ spec.subspecs.map do |subspec|
288
+ [subspec, *mapper.call(subspec)]
289
+ end.flatten
290
+ end
291
+ @recursive_subspecs = mapper.call self
292
+ end
293
+ @recursive_subspecs
166
294
  end
167
295
 
168
296
  def subspec_by_name(name)
297
+ return self if name.nil? || name == self.name
169
298
  # Remove this spec's name from the beginning of the name we’re looking for
170
299
  # and take the first component from the remainder, which is the spec we need
171
300
  # to find now.
@@ -175,38 +304,24 @@ module Pod
175
304
  # If this was the last component in the name, then return the subspec,
176
305
  # otherwise we recursively keep calling subspec_by_name until we reach the
177
306
  # last one and return that
178
- remainder.empty? ? subspec : subspec.subspec_by_name(name)
179
- end
180
-
181
- def ==(other)
182
- object_id == other.object_id ||
183
- (self.class === other &&
184
- name && name == other.name &&
185
- version && version == other.version)
186
- end
187
307
 
188
- def dependency_by_top_level_spec_name(name)
189
- @dependencies.find { |d| d.top_level_spec_name == name }
308
+ raise Informative, "Unable to find a subspec named `#{name}'." unless subspec
309
+ remainder.empty? ? subspec : subspec.subspec_by_name(name)
190
310
  end
191
311
 
192
- def part_of_specification_set
193
- if part_of
194
- Set.by_specification_name(part_of.name)
195
- end
312
+ def local?
313
+ !source.nil? && !source[:local].nil?
196
314
  end
197
315
 
198
- # Returns the specification for the pod that this pod's source is a part of.
199
- def part_of_specification
200
- (set = part_of_specification_set) && set.specification
316
+ def local_path
317
+ Pathname.new(File.expand_path(source[:local]))
201
318
  end
202
319
 
203
320
  def pod_destroot
204
- if part_of_other_pod?
205
- part_of_specification.pod_destroot
206
- elsif local?
321
+ if local?
207
322
  local_path
208
323
  else
209
- config.project_pods_root + @name
324
+ config.project_pods_root + top_level_parent.name
210
325
  end
211
326
  end
212
327
 
@@ -216,15 +331,7 @@ module Pod
216
331
  end
217
332
  end
218
333
 
219
- def part_of_other_pod?
220
- !part_of.nil?
221
- end
222
-
223
- def podfile?
224
- false
225
- end
226
-
227
- def pattern_list(patterns)
334
+ def self.pattern_list(patterns)
228
335
  if patterns.is_a?(Array) && (!defined?(Rake) || !patterns.is_a?(Rake::FileList))
229
336
  patterns
230
337
  else
@@ -232,121 +339,14 @@ module Pod
232
339
  end
233
340
  end
234
341
 
235
- # Returns all resource files of this pod, but relative to the
236
- # project pods root.
237
- def expanded_resources
238
- files = []
239
- resources.each do |pattern|
240
- pattern = pod_destroot + pattern
241
- pattern.glob.each do |file|
242
- files << file.relative_path_from(config.project_pods_root)
243
- end
244
- end
245
- files
246
- end
247
-
248
- # Returns full paths to clean for this pod.
249
- def expanded_clean_paths
250
- files = []
251
- clean_paths.each do |pattern|
252
- pattern = pod_destroot + pattern
253
- pattern.glob.each do |file|
254
- files << file
255
- end
256
- end
257
- files
258
- end
259
-
260
- # Returns all source files of this pod including header files,
261
- # but relative to the project pods root.
262
- #
263
- # If the pattern is the path to a directory, the pattern will
264
- # automatically glob for c, c++, Objective-C, and Objective-C++
265
- # files.
266
- def expanded_source_files
267
- files = []
268
- source_files.each do |pattern|
269
- pattern = pod_destroot + pattern
270
- pattern = pattern + '*.{h,m,mm,c,cpp}' if pattern.directory?
271
- pattern.glob.each do |file|
272
- files << file.relative_path_from(config.project_pods_root)
273
- end
274
- end
275
- files
276
- end
277
-
278
- def implementation_files
279
- expanded_source_files.select { |f| f.extname != '.h' }
280
- end
281
-
282
- # Returns only the header files of this pod.
283
- def header_files
284
- expanded_source_files.select { |f| f.extname == '.h' }
285
- end
286
-
287
342
  # This method takes a header path and returns the location it should have
288
343
  # in the pod's header dir.
289
344
  #
290
345
  # By default all headers are copied to the pod's header dir without any
291
- # namespacing. You can, however, override this method in the podspec, or
292
- # copy_header_mappings for full control.
346
+ # namespacing. However if the top level attribute accessor header_mappings_dir
347
+ # is specified the namespacing will be preserved from that directory.
293
348
  def copy_header_mapping(from)
294
- from.basename
295
- end
296
-
297
- # See copy_header_mapping.
298
- def copy_header_mappings
299
- header_files.inject({}) do |mappings, from|
300
- from_without_prefix = from.relative_path_from(pod_destroot_name)
301
- to = header_dir + copy_header_mapping(from_without_prefix)
302
- (mappings[to.dirname] ||= []) << from
303
- mappings
304
- end
305
- end
306
-
307
- # Returns a list of search paths where the pod's headers can be found. This
308
- # includes the pod's header dir root and any other directories that might
309
- # have been added by overriding the copy_header_mapping/copy_header_mappings
310
- # methods.
311
- def header_search_paths
312
- dirs = [header_dir] + copy_header_mappings.keys
313
- dirs.map { |dir| %{"$(PODS_ROOT)/Headers/#{dir}"} }
314
- end
315
-
316
- def to_s
317
- "#{name} (#{version})"
318
- end
319
-
320
- def inspect
321
- "#<#{self.class.name} for #{to_s}>"
322
- end
323
-
324
- def validate!
325
- missing = []
326
- missing << "`name'" unless name
327
- missing << "`version'" unless version
328
- missing << "`summary'" unless summary
329
- missing << "`homepage'" unless homepage
330
- missing << "`author(s)'" unless authors
331
- missing << "either `source' or `part_of'" unless source || part_of
332
- missing << "`source_files'" if source_files.empty? && subspecs.empty?
333
- # TODO
334
- # * validate subspecs
335
-
336
- incorrect = []
337
- allowed = [nil, :ios, :osx]
338
- incorrect << ["`platform'", allowed] unless allowed.include?(platform.name)
339
-
340
- no_errors_found = missing.empty? && incorrect.empty?
341
-
342
- unless no_errors_found
343
- message = "The following #{(missing + incorrect).size == 1 ? 'attribute is' : 'attributes are'}:\n"
344
- message << "* missing: #{missing.join(", ")}" unless missing.empty?
345
- message << "* incorrect: #{incorrect.map { |x| "#{x[0]} (#{x[1..-1]})" }.join(", ")}" unless incorrect.empty?
346
- raise Informative, message
347
- end
348
-
349
- no_errors_found
349
+ header_mappings_dir ? from.relative_path_from(header_mappings_dir) : from.basename
350
350
  end
351
351
 
352
352
  # This is a convenience method which gets called after all pods have been
@@ -365,53 +365,76 @@ module Pod
365
365
  def post_install(target)
366
366
  end
367
367
 
368
- class Subspec < Specification
369
- attr_reader :parent
368
+ def podfile?
369
+ false
370
+ end
370
371
 
371
- def initialize(parent, name)
372
- @parent, @name = parent, name
373
- # TODO a MacRuby bug, the correct super impl `initialize' is not called consistently
374
- #super(&block)
375
- post_initialize
372
+ # This is used by the specification set
373
+ def dependency_by_top_level_spec_name(name)
374
+ external_dependencies(true).each do |dep|
375
+ return dep if dep.top_level_spec_name == name
376
+ end
377
+ end
376
378
 
377
- # A subspec is _always_ part of the source of its top level spec.
378
- self.part_of = top_level_parent.name, version
379
- # A subspec has a dependency on the parent if the parent is a subspec too.
380
- dependency(@parent.name, version) if @parent.is_a?(Subspec)
379
+ def to_s
380
+ "#{name} (#{version})"
381
+ end
381
382
 
382
- yield self if block_given?
383
- end
383
+ def inspect
384
+ "#<#{self.class.name} for #{to_s}>"
385
+ end
384
386
 
385
- undef_method :name=, :version=, :source=, :defined_in_set=
387
+ def ==(other)
388
+ object_id == other.object_id ||
389
+ (self.class === other &&
390
+ name && name == other.name &&
391
+ version && version == other.version)
392
+ end
386
393
 
387
- def top_level_parent
388
- top_level_parent = @parent
389
- top_level_parent = top_level_parent.parent while top_level_parent.is_a?(Subspec)
390
- top_level_parent
391
- end
394
+ # Returns whether the specification is supported in a given platform
395
+ def supports_platform?(*platform)
396
+ platform = platform[0].is_a?(Platform) ? platform[0] : Platform.new(*platform)
397
+ available_platforms.any? { |p| platform.supports?(p) }
398
+ end
392
399
 
393
- def name
394
- "#{@parent.name}/#{@name}"
395
- end
400
+ # Defines the active platform for comsumption of the specification and
401
+ # returns self for method chainability.
402
+ # The active platform must the the same accross the chain so attributes
403
+ # that are inherited can be correctly resolved.
404
+ def activate_platform(*platform)
405
+ platform = platform[0].is_a?(Platform) ? platform[0] : Platform.new(*platform)
406
+ raise Informative, "#{to_s} is not compatible with #{platform}." unless supports_platform?(platform)
407
+ top_level_parent.active_platform = platform.to_sym
408
+ self
409
+ end
396
410
 
397
- # TODO manually forwarding the attributes that we have so far needed to forward,
398
- # but need to think if there's a better way to do this.
411
+ top_attr_accessor :active_platform
399
412
 
400
- def summary
401
- @summary ? @summary : top_level_parent.summary
402
- end
413
+ ### Not attributes
403
414
 
404
- # Override the getters to always return the value of the top level parent spec.
405
- [:version, :summary, :platform, :license, :authors, :requires_arc, :compiler_flags, :defined_in_set].each do |attr|
406
- define_method(attr) { top_level_parent.send(attr) }
407
- end
415
+ # @visibility private
416
+ #
417
+ # This is used by PlatformProxy to assign attributes for the scoped platform.
418
+ def _on_platform(platform)
419
+ before, @define_for_platforms = @define_for_platforms, [platform]
420
+ yield
421
+ ensure
422
+ @define_for_platforms = before
423
+ end
408
424
 
409
- def copy_header_mapping(from)
410
- top_level_parent.copy_header_mapping(from)
411
- end
425
+ # @visibility private
426
+ #
427
+ # This is multi-platform and to support
428
+ # subspecs with different platforms is is resolved as the
429
+ # first non nil value accross the chain.
430
+ def deployment_target=(version)
431
+ raise Informative, "The deployment target must be defined per platform like `s.ios.deployment_target = '5.0'`." unless @define_for_platforms.count == 1
432
+ @deployment_target[@define_for_platforms.first] = version
412
433
  end
413
434
 
435
+ def deployment_target(platform)
436
+ @deployment_target[platform] || ( @parent ? @parent.deployment_target(platform) : nil )
437
+ end
414
438
  end
415
-
416
439
  Spec = Specification
417
440
  end