cocoapods-core 0.17.0.rc1

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