cocoapods-core 0.17.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 (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +36 -0
  4. data/lib/cocoapods-core/core_ui.rb +19 -0
  5. data/lib/cocoapods-core/dependency.rb +295 -0
  6. data/lib/cocoapods-core/gem_version.rb +6 -0
  7. data/lib/cocoapods-core/lockfile.rb +440 -0
  8. data/lib/cocoapods-core/platform.rb +171 -0
  9. data/lib/cocoapods-core/podfile/dsl.rb +459 -0
  10. data/lib/cocoapods-core/podfile/target_definition.rb +503 -0
  11. data/lib/cocoapods-core/podfile.rb +345 -0
  12. data/lib/cocoapods-core/requirement.rb +15 -0
  13. data/lib/cocoapods-core/source/validator.rb +183 -0
  14. data/lib/cocoapods-core/source.rb +284 -0
  15. data/lib/cocoapods-core/specification/consumer.rb +356 -0
  16. data/lib/cocoapods-core/specification/dsl/attribute.rb +245 -0
  17. data/lib/cocoapods-core/specification/dsl/attribute_support.rb +76 -0
  18. data/lib/cocoapods-core/specification/dsl/deprecations.rb +47 -0
  19. data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +67 -0
  20. data/lib/cocoapods-core/specification/dsl.rb +1110 -0
  21. data/lib/cocoapods-core/specification/linter.rb +436 -0
  22. data/lib/cocoapods-core/specification/root_attribute_accessors.rb +152 -0
  23. data/lib/cocoapods-core/specification/set/presenter.rb +229 -0
  24. data/lib/cocoapods-core/specification/set/statistics.rb +277 -0
  25. data/lib/cocoapods-core/specification/set.rb +171 -0
  26. data/lib/cocoapods-core/specification/yaml.rb +60 -0
  27. data/lib/cocoapods-core/specification.rb +592 -0
  28. data/lib/cocoapods-core/standard_error.rb +84 -0
  29. data/lib/cocoapods-core/vendor/dependency.rb +264 -0
  30. data/lib/cocoapods-core/vendor/requirement.rb +208 -0
  31. data/lib/cocoapods-core/vendor/version.rb +333 -0
  32. data/lib/cocoapods-core/vendor.rb +56 -0
  33. data/lib/cocoapods-core/version.rb +99 -0
  34. data/lib/cocoapods-core/yaml_converter.rb +202 -0
  35. data/lib/cocoapods-core.rb +23 -0
  36. metadata +154 -0
@@ -0,0 +1,60 @@
1
+ module Pod
2
+ class Specification
3
+ module YAMLSupport
4
+
5
+ # @return [String] the yaml representation of the specification.
6
+ #
7
+ def to_yaml
8
+ to_hash.to_yaml
9
+ end
10
+
11
+ # @return [Hash] the hash representation of the specification including
12
+ # subspecs.
13
+ #
14
+ def to_hash
15
+ hash = attributes_hash.dup
16
+ hash["subspecs"] = subspecs.map { |spec| spec.to_hash } unless subspecs.empty?
17
+ hash
18
+ end
19
+
20
+ # @return [Bool] Whether the specification can be converted to a hash
21
+ # without loss of information.
22
+ #
23
+ def safe_to_hash?
24
+ pre_install_callback.nil? && post_install_callback.nil?
25
+ end
26
+
27
+ end
28
+
29
+ # Configures a new specification from the given hash.
30
+ #
31
+ # @param [Hash] the hash which contains the information of the
32
+ # specification.
33
+ #
34
+ # @return [Specification] the specification
35
+ #
36
+ def self.from_hash(hash)
37
+ spec = Spec.new
38
+ attributes_hash = hash.dup
39
+ subspecs = attributes_hash.delete('subspecs')
40
+ spec.attributes_hash = attributes_hash
41
+ if subspecs
42
+ spec.subspecs = subspecs.map { |hash| Specification.from_hash(hash) }
43
+ end
44
+ spec
45
+ end
46
+
47
+ # Configures a new specification from the given YAML representation.
48
+ #
49
+ # @param [String] the YAML encoded hash which contains the information of
50
+ # the specification.
51
+ #
52
+ #
53
+ # @return [Specification] the specification
54
+ #
55
+ def self.from_yaml(yaml)
56
+ hash = YAML.load(yaml)
57
+ from_hash(hash)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,592 @@
1
+ require 'cocoapods-core/specification/consumer'
2
+ require 'cocoapods-core/specification/dsl'
3
+ require 'cocoapods-core/specification/linter'
4
+ require 'cocoapods-core/specification/root_attribute_accessors'
5
+ require 'cocoapods-core/specification/set'
6
+ require 'cocoapods-core/specification/yaml'
7
+
8
+ # TODO Temporary support
9
+ if RUBY_VERSION >= "1.9"
10
+ require 'rake/file_list'
11
+ else
12
+ require 'rake'
13
+ end
14
+
15
+ module Pod
16
+
17
+ # The Specification provides a DSL to describe a Pod. A pod is defined as a
18
+ # library originating from a source. A specification can support detailed
19
+ # attributes for modules of code through subspecs.
20
+ #
21
+ # Usually it is stored in files with `podspec` extension.
22
+ #
23
+ class Specification
24
+
25
+ include Pod::Specification::DSL
26
+ include Pod::Specification::DSL::Deprecations
27
+ include Pod::Specification::RootAttributesAccessors
28
+ include Pod::Specification::YAMLSupport
29
+
30
+ # @return [Specification] the parent of the specification unless the
31
+ # specification is a root.
32
+ #
33
+ attr_reader :parent
34
+
35
+ # @param [Specification] parent @see parent
36
+ #
37
+ # @param [String] name
38
+ # the name of the specification.
39
+ #
40
+ def initialize(parent = nil, name = nil)
41
+ @attributes_hash = {}
42
+ @subspecs = []
43
+ @consumers = {}
44
+ @parent = parent
45
+ attributes_hash['name'] = name
46
+
47
+ yield self if block_given?
48
+ end
49
+
50
+ # @return [Hash] the hash that stores the information of the attributes of
51
+ # the specification.
52
+ #
53
+ attr_accessor :attributes_hash
54
+
55
+ # @return [Array<Specification>] The subspecs of the specification.
56
+ #
57
+ attr_accessor :subspecs
58
+
59
+ # Checks if a specification is equal to the given one according its name
60
+ # and to its version.
61
+ #
62
+ # @param [Specification] other
63
+ # the specification to compare with.
64
+ #
65
+ # @todo Not sure if comparing only the name and the version is the way to
66
+ # go. This is used by the installer to group specifications by root
67
+ # spec.
68
+ #
69
+ # @return [Bool] whether the specifications are equal.
70
+ #
71
+ def ==(other)
72
+ # TODO
73
+ # self.class === other &&
74
+ # attributes_hash == other.attributes_hash &&
75
+ # subspecs == other.subspecs &&
76
+ # pre_install_callback == other.pre_install_callback &&
77
+ # post_install_callback == other.post_install_callback
78
+ self.to_s == other.to_s
79
+ end
80
+
81
+ # @see ==
82
+ #
83
+ def eql?(other)
84
+ self == other
85
+ end
86
+
87
+ # Return the hash value for this specification according to its attributes
88
+ # hash.
89
+ #
90
+ # @note This function must have the property that a.eql?(b) implies
91
+ # a.hash == b.hash.
92
+ #
93
+ # @note This method is used by the Hash class.
94
+ #
95
+ # @return [Fixnum] The hash value.
96
+ #
97
+ def hash
98
+ to_s.hash
99
+ end
100
+
101
+ # @return [String] A string suitable for representing the specification in
102
+ # clients.
103
+ #
104
+ def to_s
105
+ "#{name} (#{version})"
106
+ end
107
+
108
+ # @return [String] A string suitable for debugging.
109
+ #
110
+ def inspect
111
+ "#<#{self.class.name} name=#{name.inspect}>"
112
+ end
113
+
114
+ # @param [String] string_representation
115
+ # the string that describes a {Specification} generated from
116
+ # {Specification#to_s}.
117
+ #
118
+ # @example Input examples
119
+ #
120
+ # "libPusher (1.0)"
121
+ # "libPusher (HEAD based on 1.0)"
122
+ # "RestKit/JSON (1.0)"
123
+ #
124
+ # @return [Array<String, Version>] the name and the version of a
125
+ # pod.
126
+ #
127
+ def self.name_and_version_from_string(string_reppresenation)
128
+ match_data = string_reppresenation.match(/(\S*) \((.*)\)/)
129
+ name = match_data[1]
130
+ vers = Version.new(match_data[2])
131
+ [name, vers]
132
+ end
133
+
134
+ # Returns the root name of a specification.
135
+ #
136
+ # @param [String] the name of a specification or of a subspec.
137
+ #
138
+ # @return [String] the root name
139
+ #
140
+ def self.root_name(full_name)
141
+ full_name.split('/').first
142
+ end
143
+
144
+ #-------------------------------------------------------------------------#
145
+
146
+ public
147
+
148
+ # @!group Hierarchy
149
+
150
+ # @return [Specification] The root specification or itself if it is root.
151
+ #
152
+ def root
153
+ parent ? parent.root : self
154
+ end
155
+
156
+ # @return [Bool] whether the specification is root.
157
+ #
158
+ def root?
159
+ parent.nil?
160
+ end
161
+
162
+ # @return [Bool] whether the specification is a subspec.
163
+ #
164
+ def subspec?
165
+ !parent.nil?
166
+ end
167
+
168
+ #-------------------------------------------------------------------------#
169
+
170
+ public
171
+
172
+ # @!group Dependencies & Subspecs
173
+
174
+ # @return [Array<Specifications>] the recursive list of all the subspecs of
175
+ # a specification.
176
+ #
177
+ def recursive_subspecs
178
+ mapper = lambda do |spec|
179
+ spec.subspecs.map do |subspec|
180
+ [subspec, *mapper.call(subspec)]
181
+ end.flatten
182
+ end
183
+ mapper.call(self)
184
+ end
185
+
186
+ # Returns the subspec with the given name or the receiver if the name is
187
+ # nil or equal to the name of the receiver.
188
+ #
189
+ # @param [String] relative_name
190
+ # the relative name of the subspecs starting from the receiver
191
+ # including the name of the receiver.
192
+ #
193
+ # @example Retrieving a subspec
194
+ #
195
+ # s.subspec_by_name('Pod/subspec').name #=> 'subspec'
196
+ #
197
+ # @return [Specification] the subspec with the given name or self.
198
+ #
199
+ def subspec_by_name(relative_name)
200
+ if relative_name.nil? || relative_name == base_name
201
+ self
202
+ else
203
+ remainder = relative_name[base_name.size+1..-1]
204
+ subspec_name = remainder.split('/').shift
205
+ subspec = subspecs.find { |s| s.name == "#{self.name}/#{subspec_name}" }
206
+ unless subspec
207
+ raise StandardError, "Unable to find a specification named " \
208
+ "`#{relative_name}` in `#{self.name}`."
209
+ end
210
+ subspec.subspec_by_name(remainder)
211
+ end
212
+ end
213
+
214
+ # @return [String] the name of the default subspec if provided.
215
+ #
216
+ def default_subspec
217
+ attributes_hash["default_subspec"]
218
+ end
219
+
220
+ # Returns the dependencies on subspecs.
221
+ #
222
+ # @note A specification has a dependency on either the
223
+ # {#default_subspec} or each of its children subspecs that are
224
+ # compatible with its platform.
225
+ #
226
+ # @return [Array<Dependency>] the dependencies on subspecs.
227
+ #
228
+ def subspec_dependencies(platform = nil)
229
+ if default_subspec
230
+ specs = [subspec_by_name("#{name}/#{default_subspec}")]
231
+ else
232
+ specs = subspecs.compact
233
+ end
234
+ if platform
235
+ specs = specs.select { |s| s.supported_on_platform?(platform) }
236
+ end
237
+ specs = specs.map { |s| Dependency.new(s.name, version) }
238
+ end
239
+
240
+ # Returns the dependencies on other Pods or subspecs of other Pods.
241
+ #
242
+ # @param [Bool] all_platforms
243
+ # whether the dependencies should be returned for all platforms
244
+ # instead of the active one.
245
+ #
246
+ # @note External dependencies are inherited by subspecs
247
+ #
248
+ # @return [Array<Dependency>] the dependencies on other Pods.
249
+ #
250
+ def dependencies(platform = nil)
251
+ if platform
252
+ consumer(platform).dependencies || []
253
+ else
254
+ available_platforms.map do |spec_platform|
255
+ consumer(spec_platform).dependencies
256
+ end.flatten.uniq
257
+ end
258
+ end
259
+
260
+ # @return [Array<Dependency>] all the dependencies of the specification.
261
+ #
262
+ def all_dependencies(platform = nil)
263
+ dependencies(platform) + subspec_dependencies(platform)
264
+ end
265
+
266
+ # Returns a consumer to access the multi-platform attributes.
267
+ #
268
+ # @param [String, Symbol, Platform] platform
269
+ # he platform of the consumer
270
+ #
271
+ # @return [Specification::Consumer] the consumer for the given platform
272
+ #
273
+ def consumer(platform)
274
+ platform = platform.to_sym
275
+ @consumers[platform] ||= Consumer.new(self, platform)
276
+ end
277
+
278
+ #-------------------------------------------------------------------------#
279
+
280
+ public
281
+
282
+ # @!group DSL helpers
283
+
284
+ # @return [Bool] whether the specification should use a directory as it
285
+ # source.
286
+ #
287
+ def local?
288
+ !source.nil? && !source[:local].nil?
289
+ end
290
+
291
+ # @return [Bool] whether the specification is supported in the given
292
+ # platform.
293
+ #
294
+ # @overload supported_on_platform?(platform)
295
+ #
296
+ # @param [Platform] platform
297
+ # the platform which is checked for support.
298
+ #
299
+ # @overload supported_on_platform?(symbolic_name, deployment_target)
300
+ #
301
+ # @param [Symbol] symbolic_name
302
+ # the name of the platform which is checked for support.
303
+ #
304
+ # @param [String] deployment_target
305
+ # the deployment target which is checked for support.
306
+ #
307
+ def supported_on_platform?(*platform)
308
+ platform = Platform.new(*platform)
309
+ available_platforms.any? { |available| platform.supports?(available) }
310
+ end
311
+
312
+ # @return [Array<Platform>] The platforms that the Pod is supported on.
313
+ #
314
+ # @note If no platform is specified, this method returns all known
315
+ # platforms.
316
+ #
317
+ def available_platforms
318
+ names = supported_platform_names
319
+ names = PLATFORMS if names.empty?
320
+ names.map { |name| Platform.new(name, deployment_target(name)) }
321
+ end
322
+
323
+ # Returns the deployment target for the specified platform.
324
+ #
325
+ # @param [String] platform_name
326
+ # the symbolic name of the platform.
327
+ #
328
+ # @return [String] the deployment target
329
+ # @return [Nil] if not deployment target was specified for the platform.
330
+ #
331
+ def deployment_target(platform_name)
332
+ result = platform_hash[platform_name.to_s]
333
+ result ||= parent.deployment_target(platform_name) if parent
334
+ result
335
+ end
336
+
337
+ protected
338
+
339
+ # @return [Array[Symbol]] the symbolic name of the platform in which the
340
+ # specification is supported.
341
+ #
342
+ # @return [Nil] if the specification is supported on all the known
343
+ # platforms.
344
+ #
345
+ def supported_platform_names
346
+ result = platform_hash.keys
347
+ if result.empty? && parent
348
+ result = parent.supported_platform_names
349
+ end
350
+ result
351
+ end
352
+
353
+ # @return [Hash] the normalized hash which represents the platform
354
+ # information.
355
+ #
356
+ def platform_hash
357
+ case value = attributes_hash["platforms"]
358
+ when String
359
+ { value => nil }
360
+ when Array
361
+ result = {}
362
+ value.each do |a_value|
363
+ result[a_value] = nil
364
+ end
365
+ result
366
+ when Hash
367
+ value
368
+ else
369
+ Hash.new
370
+ end
371
+ end
372
+
373
+ #-------------------------------------------------------------------------#
374
+
375
+ public
376
+
377
+ # @!group Hooks support
378
+
379
+ # @return [Proc] the pre install callback if defined.
380
+ #
381
+ attr_reader :pre_install_callback
382
+
383
+ # @return [Proc] the post install callback if defined.
384
+ #
385
+ attr_reader :post_install_callback
386
+
387
+ # Calls the pre install callback if defined.
388
+ #
389
+ # @param [Pod::LocalPod] pod
390
+ # the local pod instance that manages the files described by this
391
+ # specification.
392
+ #
393
+ # @param [Podfile::TargetDefinition] target_definition
394
+ # the target definition that required this specification as a
395
+ # dependency.
396
+ #
397
+ # @return [Bool] whether a pre install callback was specified and it was
398
+ # called.
399
+ #
400
+ def pre_install!(pod, target_definition)
401
+ return false unless @pre_install_callback
402
+ @pre_install_callback.call(pod, target_definition)
403
+ true
404
+ end
405
+
406
+ # Calls the post install callback if defined.
407
+ #
408
+ # @param [Pod::TargetInstaller] target_installer
409
+ # the target installer that is performing the installation of the
410
+ # pod.
411
+ #
412
+ # @return [Bool] whether a post install callback was specified and it was
413
+ # called.
414
+ #
415
+ def post_install!(target_installer)
416
+ return false unless @post_install_callback
417
+ @post_install_callback.call(target_installer)
418
+ true
419
+ end
420
+
421
+ #-------------------------------------------------------------------------#
422
+
423
+ public
424
+
425
+ # @!group DSL attribute writers
426
+
427
+ # Sets the value for the attribute with the given name.
428
+ #
429
+ # @param [Symbol] name
430
+ # the name of the attribute.
431
+ #
432
+ # @param [Object] value
433
+ # the value to store.
434
+ #
435
+ # @param [Symbol] platform.
436
+ # If provided the attribute is stored only for the given platform.
437
+ #
438
+ # @note If the provides value is Hash the keys are converted to a string.
439
+ #
440
+ # @return void
441
+ #
442
+ def store_attribute(name, value, platform_name = nil)
443
+ name = name.to_s
444
+ value = convert_keys_to_string(value) if value.is_a?(Hash)
445
+ if platform_name
446
+ platform_name = platform_name.to_s
447
+ attributes_hash[platform_name] ||= {}
448
+ attributes_hash[platform_name][name] = value
449
+ else
450
+ attributes_hash[name] = value
451
+ end
452
+ end
453
+
454
+ # Defines the setters methods for the attributes providing support for the
455
+ # Ruby DSL.
456
+ #
457
+ DSL.attributes.values.each do |a|
458
+ define_method(a.writer_name) do |value|
459
+ store_attribute(a.name, value)
460
+ end
461
+
462
+ if a.writer_singular_form
463
+ alias_method(a.writer_singular_form, a.writer_name)
464
+ end
465
+ end
466
+
467
+ private
468
+
469
+ # Converts the keys of the given hash to a string.
470
+ #
471
+ # @param [Object] value
472
+ # the value that needs to be stripped from the Symbols.
473
+ #
474
+ # @return [Hash] the hash with the strings instead of the keys.
475
+ #
476
+ def convert_keys_to_string(value)
477
+ return unless value
478
+ result = {}
479
+ value.each do |key, subvalue|
480
+ subvalue = convert_keys_to_string(subvalue) if subvalue.is_a?(Hash)
481
+ result[key.to_s] = subvalue
482
+ end
483
+ result
484
+ end
485
+
486
+ #-------------------------------------------------------------------------#
487
+
488
+ public
489
+
490
+ # @!group File representation
491
+
492
+ # @return [String] The SHA1 digest of the file in which the specification
493
+ # is defined.
494
+ #
495
+ # @return [Nil] If the specification is not defined in a file.
496
+ #
497
+ def checksum
498
+ require 'digest'
499
+ unless defined_in_file.nil?
500
+ checksum = Digest::SHA1.hexdigest(File.read(defined_in_file))
501
+ checksum = checksum.encode('UTF-8') if checksum.respond_to?(:encode)
502
+ checksum
503
+ end
504
+ end
505
+
506
+ # @return [String] the path where the specification is defined, if loaded
507
+ # from a file.
508
+ #
509
+ def defined_in_file
510
+ root? ? @defined_in_file : root.defined_in_file
511
+ end
512
+
513
+ # Loads a specification form the given path.
514
+ #
515
+ # @param [Pathname, String] path
516
+ # the path of the `podspec` file.
517
+ #
518
+ # @param [String] subspec_name
519
+ # the name of the specification that should be returned. If it is
520
+ # nil returns the root specification.
521
+ #
522
+ # @raise If the file doesn't return a Pods::Specification after
523
+ # evaluation.
524
+ #
525
+ # @return [Specification] the specification
526
+ #
527
+ def self.from_file(path, subspec_name = nil)
528
+ path = Pathname.new(path)
529
+ unless path.exist?
530
+ raise StandardError, "No podspec exists at path `#{path}`."
531
+ end
532
+
533
+ case path.extname
534
+ when '.podspec'
535
+ spec = ::Pod._eval_podspec(path)
536
+ unless spec.is_a?(Specification)
537
+ raise StandardError, "Invalid podspec file at path `#{path}`."
538
+ end
539
+ when '.yaml'
540
+ spec = Specification.from_yaml(path.read)
541
+ else
542
+ raise StandardError, "Unsupported specification format `#{path.extname}`."
543
+ end
544
+
545
+ spec.defined_in_file = path
546
+ spec.subspec_by_name(subspec_name)
547
+ end
548
+
549
+ # Sets the path of the `podspec` file used to load the specification.
550
+ #
551
+ # @param [String] file
552
+ # the `podspec` file.
553
+ #
554
+ # @return [void]
555
+ #
556
+ # @visibility private
557
+ #
558
+ def defined_in_file=(file)
559
+ unless root?
560
+ raise StandardError, "Defined in file can be set only for root specs."
561
+ end
562
+ @defined_in_file = file
563
+ end
564
+ end
565
+
566
+
567
+ #---------------------------------------------------------------------------#
568
+
569
+ Spec = Specification
570
+
571
+ # Evaluates the file at the given path in the namespace of the Pod module.
572
+ #
573
+ # @return [Object] it can return any object but, is expected to be called on
574
+ # `podspec` files that should return a #{Specification}.
575
+ #
576
+ # @private
577
+ #
578
+ def self._eval_podspec(path)
579
+ string = File.open(path, 'r:utf-8') { |f| f.read }
580
+ # Work around for Rubinius incomplete encoding in 1.9 mode
581
+ if string.respond_to?(:encoding) && string.encoding.name != "UTF-8"
582
+ string.encode!('UTF-8')
583
+ end
584
+
585
+ begin
586
+ eval(string, nil, path.to_s)
587
+ rescue Exception => e
588
+ raise DSLError.new("Invalid `#{path.basename}` file: #{e.message}",
589
+ path, e.backtrace)
590
+ end
591
+ end
592
+ end