cocoapods 0.5.1 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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