cocoapods-core 0.30.0 → 1.15.2

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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +7 -10
  3. data/lib/cocoapods-core/build_type.rb +121 -0
  4. data/lib/cocoapods-core/cdn_source.rb +501 -0
  5. data/lib/cocoapods-core/core_ui.rb +4 -3
  6. data/lib/cocoapods-core/dependency.rb +100 -73
  7. data/lib/cocoapods-core/gem_version.rb +1 -2
  8. data/lib/cocoapods-core/github.rb +32 -5
  9. data/lib/cocoapods-core/http.rb +86 -0
  10. data/lib/cocoapods-core/lockfile.rb +161 -56
  11. data/lib/cocoapods-core/metrics.rb +47 -0
  12. data/lib/cocoapods-core/platform.rb +99 -11
  13. data/lib/cocoapods-core/podfile/dsl.rb +623 -124
  14. data/lib/cocoapods-core/podfile/target_definition.rb +662 -109
  15. data/lib/cocoapods-core/podfile.rb +138 -65
  16. data/lib/cocoapods-core/requirement.rb +37 -8
  17. data/lib/cocoapods-core/source/acceptor.rb +16 -13
  18. data/lib/cocoapods-core/source/aggregate.rb +79 -103
  19. data/lib/cocoapods-core/source/health_reporter.rb +9 -18
  20. data/lib/cocoapods-core/source/manager.rb +488 -0
  21. data/lib/cocoapods-core/source/metadata.rb +79 -0
  22. data/lib/cocoapods-core/source.rb +241 -70
  23. data/lib/cocoapods-core/specification/consumer.rb +187 -47
  24. data/lib/cocoapods-core/specification/dsl/attribute.rb +49 -85
  25. data/lib/cocoapods-core/specification/dsl/attribute_support.rb +6 -8
  26. data/lib/cocoapods-core/specification/dsl/deprecations.rb +9 -126
  27. data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +30 -20
  28. data/lib/cocoapods-core/specification/dsl.rb +943 -296
  29. data/lib/cocoapods-core/specification/json.rb +64 -23
  30. data/lib/cocoapods-core/specification/linter/analyzer.rb +218 -0
  31. data/lib/cocoapods-core/specification/linter/result.rb +128 -0
  32. data/lib/cocoapods-core/specification/linter.rb +310 -309
  33. data/lib/cocoapods-core/specification/root_attribute_accessors.rb +90 -39
  34. data/lib/cocoapods-core/specification/set/presenter.rb +35 -71
  35. data/lib/cocoapods-core/specification/set.rb +42 -96
  36. data/lib/cocoapods-core/specification.rb +368 -130
  37. data/lib/cocoapods-core/standard_error.rb +45 -24
  38. data/lib/cocoapods-core/trunk_source.rb +14 -0
  39. data/lib/cocoapods-core/vendor/requirement.rb +133 -53
  40. data/lib/cocoapods-core/vendor/version.rb +197 -156
  41. data/lib/cocoapods-core/vendor.rb +1 -5
  42. data/lib/cocoapods-core/version.rb +137 -42
  43. data/lib/cocoapods-core/yaml_helper.rb +334 -0
  44. data/lib/cocoapods-core.rb +10 -4
  45. metadata +100 -27
  46. data/lib/cocoapods-core/source/abstract_data_provider.rb +0 -71
  47. data/lib/cocoapods-core/source/file_system_data_provider.rb +0 -150
  48. data/lib/cocoapods-core/source/github_data_provider.rb +0 -143
  49. data/lib/cocoapods-core/specification/set/statistics.rb +0 -266
  50. data/lib/cocoapods-core/yaml_converter.rb +0 -192
@@ -1,6 +1,5 @@
1
1
  module Pod
2
2
  class Podfile
3
-
4
3
  # The TargetDefinition stores the information of a CocoaPods static
5
4
  # library. The target definition can be linked with one or more targets of
6
5
  # the user project.
@@ -9,7 +8,6 @@ module Pod
9
8
  # of the parent.
10
9
  #
11
10
  class TargetDefinition
12
-
13
11
  # @return [TargetDefinition, Podfile] the parent target definition or the
14
12
  # Podfile if the receiver is root.
15
13
  #
@@ -21,17 +19,12 @@ module Pod
21
19
  # @param [TargetDefinition] parent
22
20
  # @see parent
23
21
  #
24
- # @option options [Bool] :exclusive
25
- # @see exclusive?
26
- #
27
22
  def initialize(name, parent, internal_hash = nil)
28
23
  @internal_hash = internal_hash || {}
29
24
  @parent = parent
30
25
  @children = []
31
-
32
- unless internal_hash
33
- self.name = name
34
- end
26
+ @label = nil
27
+ self.name ||= name
35
28
  if parent.is_a?(TargetDefinition)
36
29
  parent.children << self
37
30
  end
@@ -48,7 +41,7 @@ module Pod
48
41
  (children + children.map(&:recursive_children)).flatten
49
42
  end
50
43
 
51
- # @return [Bool] Whether the target definition is root.
44
+ # @return [Boolean] Whether the target definition is root.
52
45
  #
53
46
  def root?
54
47
  parent.is_a?(Podfile) || parent.nil?
@@ -75,13 +68,27 @@ module Pod
75
68
  # definition including the inherited ones.
76
69
  #
77
70
  def dependencies
78
- if exclusive? || parent.nil?
71
+ if exclusive?
79
72
  non_inherited_dependencies
80
73
  else
81
74
  non_inherited_dependencies + parent.dependencies
82
75
  end
83
76
  end
84
77
 
78
+ # @return [Array<TargetDefinition>] the targets from which this target
79
+ # definition should inherit only search paths.
80
+ #
81
+ def targets_to_inherit_search_paths
82
+ can_inherit = !root? && matches_platform?(parent)
83
+ if inheritance == 'search_paths' # && can_inherit
84
+ parent.targets_to_inherit_search_paths << parent
85
+ elsif can_inherit
86
+ parent.targets_to_inherit_search_paths
87
+ else
88
+ []
89
+ end
90
+ end
91
+
85
92
  # @return [Array] The list of the dependencies of the target definition,
86
93
  # excluding inherited ones.
87
94
  #
@@ -89,7 +96,7 @@ module Pod
89
96
  pod_dependencies.concat(podspec_dependencies)
90
97
  end
91
98
 
92
- # @return [Bool] Whether the target definition has at least one
99
+ # @return [Boolean] Whether the target definition has at least one
93
100
  # dependency, excluding inherited ones.
94
101
  #
95
102
  def empty?
@@ -100,13 +107,13 @@ module Pod
100
107
  # name.
101
108
  #
102
109
  def label
103
- if root? && name == "Pods"
104
- "Pods"
105
- elsif exclusive? || parent.nil?
106
- "Pods-#{name}"
107
- else
108
- "#{parent.label}-#{name}"
109
- end
110
+ @label ||= if root? && name == 'Pods'
111
+ 'Pods'
112
+ elsif exclusive? || parent.nil?
113
+ "Pods-#{name}"
114
+ else
115
+ "#{parent.label}-#{name}"
116
+ end
110
117
  end
111
118
 
112
119
  alias_method :to_s, :label
@@ -133,94 +140,98 @@ module Pod
133
140
  # Sets the path of the user project this target definition should link
134
141
  # with.
135
142
  #
136
- # @param [String] path
143
+ # @param [String] name
137
144
  # The path of the project.
138
145
  #
139
146
  # @return [void]
140
147
  #
141
148
  def name=(name)
149
+ @label = nil
142
150
  set_hash_value('name', name)
143
151
  end
144
152
 
145
153
  #--------------------------------------#
146
154
 
147
- # Returns whether the target definition should inherit the dependencies
148
- # of the parent.
149
- #
150
- # @note A target is always `exclusive` if it is root.
155
+ # @return [Boolean] whether this target definition is abstract.
151
156
  #
152
- # @note A target is always `exclusive` if the `platform` does
153
- # not match the parent's `platform`.
154
- #
155
- # @return [Bool] whether is exclusive.
156
- #
157
- def exclusive?
158
- if root?
159
- true
160
- elsif get_hash_value('exclusive')
161
- true
162
- else
163
- platform && parent && parent.platform != platform
164
- end
157
+ def abstract?
158
+ get_hash_value('abstract', root?)
165
159
  end
166
160
 
167
- # Sets whether the target definition is exclusive.
161
+ # Sets whether this target definition is abstract.
168
162
  #
169
- # @param [Bool] flag
170
- # Whether the definition is exclusive.
163
+ # @param [Boolean] abstract
164
+ # whether this target definition is abstract.
171
165
  #
172
166
  # @return [void]
173
167
  #
174
- def exclusive=(flag)
175
- set_hash_value('exclusive', flag)
168
+ def abstract=(abstract)
169
+ set_hash_value('abstract', abstract)
176
170
  end
177
171
 
178
172
  #--------------------------------------#
179
173
 
180
- # @return [Array<String>] The list of the names of the Xcode targets with
181
- # which this target definition should be linked with.
174
+ # @return [String] the inheritance mode for this target definition.
182
175
  #
183
- def link_with
184
- value = get_hash_value('link_with')
185
- value unless value.nil? || value.empty?
176
+ def inheritance
177
+ get_hash_value('inheritance', 'complete')
186
178
  end
187
179
 
188
- # Sets the client targets that should be integrated by this definition.
180
+ # Sets the inheritance mode for this target definition.
181
+ #
182
+ # @param [#to_s] inheritance
183
+ # the inheritance mode for this target definition.
189
184
  #
190
- # @param [Array<String>] targets
191
- # The list of the targets names.
185
+ # @raise [Informative] if this target definition is a root target
186
+ # definition or if the `inheritance` value is unknown.
192
187
  #
193
188
  # @return [void]
194
189
  #
195
- def link_with=(targets)
196
- set_hash_value('link_with', Array(targets).map(&:to_s))
190
+ def inheritance=(inheritance)
191
+ inheritance = inheritance.to_s
192
+ unless %w(none search_paths complete).include?(inheritance)
193
+ raise Informative, "Unrecognized inheritance option `#{inheritance}` specified for target `#{name}`."
194
+ end
195
+ if root?
196
+ raise Informative, 'Cannot set inheritance for the root target definition.'
197
+ end
198
+ if abstract?
199
+ raise Informative, 'Cannot set inheritance for abstract target definition.'
200
+ end
201
+ set_hash_value('inheritance', inheritance)
197
202
  end
198
203
 
199
204
  #--------------------------------------#
200
205
 
201
- # Returns whether the target definition should link with the first target
202
- # of the project.
206
+ # Returns whether the target definition should inherit the dependencies
207
+ # of the parent.
203
208
  #
204
- # @note This option is ignored if {link_with} is set.
209
+ # @note A target is always `exclusive` if it is root.
205
210
  #
206
- # @return [Bool] whether is exclusive.
211
+ # @note A target is always `exclusive` if the `platform` does
212
+ # not match the parent's `platform`.
207
213
  #
208
- def link_with_first_target?
209
- get_hash_value('link_with_first_target') unless link_with
214
+ # @return [Boolean] whether is exclusive.
215
+ #
216
+ def exclusive?
217
+ if root?
218
+ true
219
+ else
220
+ !matches_platform?(parent) || (inheritance != 'complete')
221
+ end
210
222
  end
211
223
 
212
- # Sets whether the target definition should link with the first target of
213
- # the project.
224
+ # @param [TargetDefinition, Nil] target_definition
225
+ # the target definition to check for platform compatibility.
214
226
  #
215
- # @note This option is ignored if {link_with} is set.
227
+ # @return [Boolean]
228
+ # whether this target definition matches the platform of
229
+ # `target_definition`.
216
230
  #
217
- # @param [Bool] flag
218
- # Whether the definition should link with the first target.
219
- #
220
- # @return [void]
221
- #
222
- def link_with_first_target=(flag)
223
- set_hash_value('link_with_first_target', flag)
231
+ def matches_platform?(target_definition)
232
+ return false unless target_definition
233
+ return true if target_definition.platform == platform
234
+ !target_definition.platform && target_definition.abstract?
224
235
  end
225
236
 
226
237
  #--------------------------------------#
@@ -231,7 +242,7 @@ module Pod
231
242
  def user_project_path
232
243
  path = get_hash_value('user_project_path')
233
244
  if path
234
- File.extname(path) == '.xcodeproj' ? path : "#{path}.xcodeproj"
245
+ Pathname(path).sub_ext('.xcodeproj').to_path
235
246
  else
236
247
  parent.user_project_path unless root?
237
248
  end
@@ -277,47 +288,331 @@ module Pod
277
288
 
278
289
  #--------------------------------------#
279
290
 
280
- # @return [Bool] whether the target definition should inhibit warnings
291
+ # @return [Array<Hash>] The list of the script phases of the target definition.
292
+ #
293
+ def script_phases
294
+ get_hash_value('script_phases') || []
295
+ end
296
+
297
+ #--------------------------------------#
298
+
299
+ # @return [String] The project name to use for the given pod name or `nil` if none specified.
300
+ #
301
+ # @note When querying for a subspec then use the root pod spec name instead as this is what's stored.
302
+ #
303
+ def project_name_for_pod(pod_name)
304
+ if root?
305
+ raw_project_names_hash[pod_name]
306
+ else
307
+ raw_project_names_hash[pod_name] || parent.project_name_for_pod(pod_name)
308
+ end
309
+ end
310
+
311
+ #--------------------------------------#
312
+ #
313
+ # @return [Boolean] whether the target definition should inhibit warnings
281
314
  # for a single pod. If inhibit_all_warnings is true, it will
282
315
  # return true for any asked pod.
283
316
  #
284
317
  def inhibits_warnings_for_pod?(pod_name)
285
- if inhibit_warnings_hash['all']
318
+ if Array(inhibit_warnings_hash['not_for_pods']).include?(pod_name)
319
+ false
320
+ elsif inhibit_warnings_hash['all']
286
321
  true
287
322
  elsif !root? && parent.inhibits_warnings_for_pod?(pod_name)
288
323
  true
289
324
  else
290
- inhibit_warnings_hash['for_pods'] ||= []
291
- inhibit_warnings_hash['for_pods'].include? pod_name
325
+ Array(inhibit_warnings_hash['for_pods']).include? pod_name
292
326
  end
293
327
  end
294
328
 
295
329
  # Sets whether the target definition should inhibit the warnings during
296
330
  # compilation for all pods.
297
331
  #
298
- # @param [Bool] flag
332
+ # @param [Boolean] flag
299
333
  # Whether the warnings should be suppressed.
300
334
  #
301
335
  # @return [void]
302
336
  #
303
337
  def inhibit_all_warnings=(flag)
304
- inhibit_warnings_hash['all'] = flag
338
+ raw_inhibit_warnings_hash['all'] = flag
305
339
  end
306
340
 
307
341
  # Inhibits warnings for a specific pod during compilation.
308
342
  #
309
- # @param [String] pod name
343
+ # @param [String] pod_name
344
+ # Name of the pod for which the warnings will be inhibited or not.
345
+ #
346
+ # @param [Boolean] should_inhibit
347
+ # Whether the warnings should be inhibited or not for given pod.
348
+ #
349
+ # @return [void]
350
+ #
351
+ def set_inhibit_warnings_for_pod(pod_name, should_inhibit)
352
+ hash_key = case should_inhibit
353
+ when true
354
+ 'for_pods'
355
+ when false
356
+ 'not_for_pods'
357
+ when nil
358
+ return
359
+ else
360
+ raise ArgumentError, "Got `#{should_inhibit.inspect}`, should be a boolean"
361
+ end
362
+ raw_inhibit_warnings_hash[hash_key] ||= []
363
+ raw_inhibit_warnings_hash[hash_key] << pod_name
364
+ end
365
+
366
+ #--------------------------------------#
367
+
368
+ # The (desired) build type for the pods integrated in this target definition. Defaults to static libraries and can
369
+ # only be overridden through Pod::Podfile::DSL#use_frameworks!.
370
+ #
371
+ # @return [BuildType]
372
+ #
373
+ def build_type
374
+ value = get_hash_value('uses_frameworks', root? ? BuildType.static_library : parent.build_type)
375
+ case value
376
+ when true, false
377
+ value ? BuildType.dynamic_framework : BuildType.static_library
378
+ when Hash
379
+ BuildType.new(:linkage => value.fetch(:linkage), :packaging => value.fetch(:packaging))
380
+ when BuildType
381
+ value
382
+ else
383
+ raise ArgumentError, "Got `#{value.inspect}`, should be a boolean, hash or BuildType."
384
+ end
385
+ end
386
+
387
+ # Sets whether the target definition's pods should be built as frameworks.
388
+ #
389
+ # @param [Boolean, Hash] option
390
+ # Whether pods that are integrated in this target should be built as frameworks. If the option is a
391
+ # boolean then the value affects both packaging and linkage styles. If set to true, then dynamic frameworks
392
+ # are used and if it's set to false, then static libraries are used. If the option is a hash then
393
+ # `:framework` packaging is implied and the user configures the `:linkage` style to use.
394
+ #
395
+ # @return [void]
396
+ #
397
+ def use_frameworks!(option = true)
398
+ value = case option
399
+ when true, false
400
+ option ? BuildType.dynamic_framework : BuildType.static_library
401
+ when Hash
402
+ BuildType.new(:linkage => option.fetch(:linkage), :packaging => :framework)
403
+ else
404
+ raise ArgumentError, "Got `#{option.inspect}`, should be a boolean or hash."
405
+ end
406
+ set_hash_value('uses_frameworks', value.to_hash)
407
+ end
408
+
409
+ # @return [Boolean] whether the target definition pods should be built as frameworks.
410
+ #
411
+ def uses_frameworks?
412
+ if internal_hash['uses_frameworks'].nil?
413
+ root? ? false : parent.uses_frameworks?
414
+ else
415
+ build_type.framework?
416
+ end
417
+ end
418
+
419
+ #--------------------------------------#
420
+
421
+ # Sets the Swift version that the target definition should use.
422
+ #
423
+ # @param [String] version
424
+ # The Swift version that the target definition should use.
425
+ #
426
+ # @return [void]
427
+ #
428
+ def swift_version=(version)
429
+ set_hash_value('swift_version', version)
430
+ end
431
+
432
+ # @return [String] the Swift version that the target definition should
433
+ # use.
434
+ #
435
+ def swift_version
436
+ get_hash_value('swift_version')
437
+ end
438
+
439
+ # @return [Array<String>] the Swift version requirements this target definition enforces.
440
+ #
441
+ def swift_version_requirements
442
+ get_hash_value('swift_version_requirements')
443
+ end
444
+
445
+ # Queries the target if a version of Swift is supported or not.
446
+ #
447
+ # @param [Version] swift_version
448
+ # The Swift version to query against.
449
+ #
450
+ # @return [Boolean] Whether the target accepts the specified Swift version.
451
+ #
452
+ def supports_swift_version?(swift_version)
453
+ if swift_version_requirements.nil?
454
+ root? || parent.supports_swift_version?(swift_version)
455
+ else
456
+ Requirement.create(swift_version_requirements).satisfied_by?(swift_version)
457
+ end
458
+ end
459
+
460
+ #--------------------------------------#
461
+
462
+ # Whether a specific pod should be linked to the target when building for
463
+ # a specific configuration. If a pod has not been explicitly whitelisted
464
+ # for any configuration, it is implicitly whitelisted.
465
+ #
466
+ # @param [String] pod_name
467
+ # The pod that we're querying about inclusion for in the given
468
+ # configuration.
469
+ #
470
+ # @param [String] configuration_name
471
+ # The configuration that we're querying about inclusion of the
472
+ # pod in.
473
+ #
474
+ # @note Build configurations are case compared case-insensitively in
475
+ # CocoaPods.
476
+ #
477
+ # @return [Boolean] flag
478
+ # Whether the pod should be linked with the target
479
+ #
480
+ def pod_whitelisted_for_configuration?(pod_name, configuration_name)
481
+ found = false
482
+ configuration_pod_whitelist.each do |configuration, pods|
483
+ if pods.include?(pod_name)
484
+ found = true
485
+ if configuration.downcase == configuration_name.to_s.downcase
486
+ return true
487
+ end
488
+ end
489
+ end
490
+ !found && (root? || (inheritance != 'none' && parent.pod_whitelisted_for_configuration?(pod_name, configuration_name)))
491
+ end
492
+
493
+ # Whitelists a pod for a specific configuration. If a pod is whitelisted
494
+ # for any configuration, it will only be linked with the target in the
495
+ # configuration(s) specified. If it is not whitelisted for any
496
+ # configuration, it is implicitly included in all configurations.
497
+ #
498
+ # @param [String] pod_name
499
+ # The pod that should be included in the given configuration.
500
+ #
501
+ # @param [String, Symbol] configuration_name
502
+ # The configuration that the pod should be included in
503
+ #
504
+ # @note Build configurations are stored as a String.
505
+ #
506
+ # @return [void]
507
+ #
508
+ def whitelist_pod_for_configuration(pod_name, configuration_name)
509
+ configuration_name = configuration_name.to_s
510
+ list = raw_configuration_pod_whitelist[configuration_name] ||= []
511
+ list << pod_name
512
+ end
513
+
514
+ # @return [Array<String>] unique list of all configurations for which
515
+ # pods have been whitelisted.
516
+ #
517
+ def all_whitelisted_configurations
518
+ parent_configurations = (root? || inheritance == 'none') ? [] : parent.all_whitelisted_configurations
519
+ (configuration_pod_whitelist.keys + parent_configurations).uniq
520
+ end
521
+
522
+ #--------------------------------------#
523
+
524
+ def raw_use_modular_headers_hash
525
+ get_hash_value('use_modular_headers', {})
526
+ end
527
+ private :raw_use_modular_headers_hash
528
+
529
+ # Returns the use_modular_headers hash pre-populated with default values.
530
+ #
531
+ # @return [Hash<String, Array>] Hash with :all key for building all
532
+ # pods as modules, :for_pods key for building as module per Pod,
533
+ # and :not_for_pods key for not biulding as module per Pod.
534
+ #
535
+ def use_modular_headers_hash
536
+ raw_hash = raw_use_modular_headers_hash
537
+ if exclusive?
538
+ raw_hash
539
+ else
540
+ parent_hash = parent.send(:use_modular_headers_hash).dup
541
+ if parent_hash['not_for_pods']
542
+ # Remove pods that are set to not use modular headers inside parent
543
+ # if they are set to use modular headers inside current target.
544
+ parent_hash['not_for_pods'] -= Array(raw_hash['for_pods'])
545
+ end
546
+ if parent_hash['for_pods']
547
+ # Remove pods that are set to use modular headers inside parent if they are set to not use modular headers inside current target.
548
+ parent_hash['for_pods'] -= Array(raw_hash['for_pods'])
549
+ end
550
+ if raw_hash['all']
551
+ # Clean pods that are set to not use modular headers inside parent if use_modular_headers! was set.
552
+ parent_hash['not_for_pods'] = nil
553
+ end
554
+ parent_hash.merge(raw_hash) do |_, l, r|
555
+ Array(l).concat(r).uniq
556
+ end
557
+ end
558
+ end
559
+
560
+ # @return [Boolean] whether the target definition should use modular headers
561
+ # for a single pod. If use_modular_headers! is true, it will
562
+ # return true for any asked pod.
563
+ #
564
+ def build_pod_as_module?(pod_name)
565
+ if Array(use_modular_headers_hash['not_for_pods']).include?(pod_name)
566
+ false
567
+ elsif use_modular_headers_hash['all']
568
+ true
569
+ elsif !root? && parent.build_pod_as_module?(pod_name)
570
+ true
571
+ else
572
+ Array(use_modular_headers_hash['for_pods']).include? pod_name
573
+ end
574
+ end
575
+
576
+ # Sets whether the target definition should use modular headers for all pods.
577
+ #
578
+ # @param [Boolean] flag
310
579
  # Whether the warnings should be suppressed.
311
580
  #
312
581
  # @return [void]
313
582
  #
314
- def inhibit_warnings_for_pod(pod_name)
315
- inhibit_warnings_hash['for_pods'] ||= []
316
- inhibit_warnings_hash['for_pods'] << pod_name
583
+ def use_modular_headers_for_all_pods=(flag)
584
+ raw_use_modular_headers_hash['all'] = flag
585
+ end
586
+
587
+ # Use modular headers for a specific pod during compilation.
588
+ #
589
+ # @param [String] pod_name
590
+ # Name of the pod for which modular headers will be used.
591
+ #
592
+ # @param [Boolean] flag
593
+ # Whether modular headers should be used.
594
+ #
595
+ # @return [void]
596
+ #
597
+ def set_use_modular_headers_for_pod(pod_name, flag)
598
+ hash_key = case flag
599
+ when true
600
+ 'for_pods'
601
+ when false
602
+ 'not_for_pods'
603
+ when nil
604
+ return
605
+ else
606
+ raise ArgumentError, "Got `#{flag.inspect}`, should be a boolean"
607
+ end
608
+ raw_use_modular_headers_hash[hash_key] ||= []
609
+ raw_use_modular_headers_hash[hash_key] << pod_name
317
610
  end
318
611
 
319
612
  #--------------------------------------#
320
613
 
614
+ PLATFORM_DEFAULTS = { :ios => '4.3', :osx => '10.6', :tvos => '9.0', :visionos => '1.0', :watchos => '2.0' }.freeze
615
+
321
616
  # @return [Platform] the platform of the target definition.
322
617
  #
323
618
  # @note If no deployment target has been specified a default value is
@@ -332,7 +627,7 @@ module Pod
332
627
  else
333
628
  name = name_or_hash.to_sym
334
629
  end
335
- target ||= (name == :ios ? '4.3' : '10.6')
630
+ target ||= PLATFORM_DEFAULTS[name]
336
631
  Platform.new(name, target)
337
632
  else
338
633
  parent.platform unless root?
@@ -352,9 +647,10 @@ module Pod
352
647
  # @return [void]
353
648
  #
354
649
  def set_platform(name, target = nil)
355
- unless [:ios, :osx].include?(name)
650
+ name = :osx if name == :macos
651
+ unless [:ios, :osx, :tvos, :visionos, :watchos].include?(name)
356
652
  raise StandardError, "Unsupported platform `#{name}`. Platform " \
357
- "must be `:ios` or `:osx`."
653
+ 'must be `:ios`, `:osx`, `:macos`, `:tvos`, :visionos, or `:watchos`.'
358
654
  end
359
655
 
360
656
  if target
@@ -365,6 +661,30 @@ module Pod
365
661
  set_hash_value('platform', value)
366
662
  end
367
663
 
664
+ # Sets the platform of the target definition.
665
+ #
666
+ # @see #set_platform
667
+ #
668
+ # @raise When the target definition already has a platform set.
669
+ #
670
+ # @return [void]
671
+ #
672
+ def set_platform!(name, target = nil)
673
+ raise StandardError, "The target `#{label}` already has a platform set." if get_hash_value('platform')
674
+ set_platform(name, target)
675
+ end
676
+
677
+ # Stores the Swift version requirements to be used for this target.
678
+ #
679
+ # @param [String, Version, Array<String>, Array<Version>] requirements
680
+ # The set of requirements this target supports.
681
+ #
682
+ # @return [void]
683
+ #
684
+ def store_swift_version_requirements(*requirements)
685
+ set_hash_value('swift_version_requirements', requirements.flatten.map(&:to_s))
686
+ end
687
+
368
688
  #--------------------------------------#
369
689
 
370
690
  # Stores the dependency for a Pod with the given name.
@@ -385,7 +705,11 @@ module Pod
385
705
  # @return [void]
386
706
  #
387
707
  def store_pod(name, *requirements)
708
+ return if parse_subspecs(name, requirements) # This parse method must be called first
388
709
  parse_inhibit_warnings(name, requirements)
710
+ parse_modular_headers(name, requirements)
711
+ parse_configuration_whitelist(name, requirements)
712
+ parse_project_name(name, requirements)
389
713
 
390
714
  if requirements && !requirements.empty?
391
715
  pod = { name => requirements }
@@ -394,6 +718,7 @@ module Pod
394
718
  end
395
719
 
396
720
  get_hash_value('dependencies', []) << pod
721
+ nil
397
722
  end
398
723
 
399
724
  #--------------------------------------#
@@ -409,20 +734,64 @@ module Pod
409
734
  # @note The storage of this information is optimized for YAML
410
735
  # readability.
411
736
  #
412
- # @todo This needs urgently a rename.
737
+ # @todo This urgently needs a rename.
413
738
  #
414
739
  # @return [void]
415
740
  #
416
741
  def store_podspec(options = nil)
417
- if options
418
- unless options.keys.all? { |key| [:name, :path].include?(key) }
419
- raise StandardError, "Unrecognized options for the podspec " \
420
- "method `#{options}`"
742
+ options ||= {}
743
+ unless options.keys.all? { |key| [:name, :path, :subspecs, :subspec].include?(key) }
744
+ raise StandardError, 'Unrecognized options for the podspec ' \
745
+ "method `#{options}`"
746
+ end
747
+ if subspec_name = options[:subspec]
748
+ unless subspec_name.is_a?(String)
749
+ raise StandardError, "Option `:subspec => #{subspec_name.inspect}` should be a String"
421
750
  end
422
- get_hash_value('podspecs', []) << options
423
- else
424
- get_hash_value('podspecs', []) << { :autodetect => true }
425
751
  end
752
+ if subspec_names = options[:subspecs]
753
+ if !subspec_names.is_a?(Array) || !subspec_names.all? { |name| name.is_a? String }
754
+ raise StandardError, "Option `:subspecs => #{subspec_names.inspect}` " \
755
+ 'should be an Array of Strings'
756
+ end
757
+ end
758
+ options[:autodetect] = true if !options.include?(:name) && !options.include?(:path)
759
+ get_hash_value('podspecs', []) << options
760
+ end
761
+
762
+ #--------------------------------------#
763
+
764
+ # Stores the script phase to add for this target definition.
765
+ #
766
+ # @param [Hash] options
767
+ # The options to use for this script phase. The required keys
768
+ # are: `:name`, `:script`, while the optional keys are:
769
+ # `:shell_path`, `:input_files`, `:output_files`, `:show_env_vars_in_log`, `:execution_position` and
770
+ # `:dependency_file`.
771
+ #
772
+ # @return [void]
773
+ #
774
+ def store_script_phase(options)
775
+ option_keys = options.keys
776
+ unrecognized_keys = option_keys - Specification::ALL_SCRIPT_PHASE_KEYS
777
+ unless unrecognized_keys.empty?
778
+ raise StandardError, "Unrecognized options `#{unrecognized_keys}` in shell script `#{options[:name]}` within `#{name}` target. " \
779
+ "Available options are `#{Specification::ALL_SCRIPT_PHASE_KEYS}`."
780
+ end
781
+ missing_required_keys = Specification::SCRIPT_PHASE_REQUIRED_KEYS - option_keys
782
+ unless missing_required_keys.empty?
783
+ raise StandardError, "Missing required shell script phase options `#{missing_required_keys.join(', ')}`"
784
+ end
785
+ script_phases_hash = get_hash_value('script_phases', [])
786
+ if script_phases_hash.map { |script_phase_options| script_phase_options[:name] }.include?(options[:name])
787
+ raise StandardError, "Script phase with name `#{options[:name]}` name already present for target `#{name}`."
788
+ end
789
+ options[:execution_position] = :any unless options.key?(:execution_position)
790
+ unless Specification::EXECUTION_POSITION_KEYS.include?(options[:execution_position])
791
+ raise StandardError, "Invalid execution position value `#{options[:execution_position]}` in shell script `#{options[:name]}` within `#{name}` target. " \
792
+ "Available options are `#{Specification::EXECUTION_POSITION_KEYS}`."
793
+ end
794
+ script_phases_hash << options
426
795
  end
427
796
 
428
797
  #-----------------------------------------------------------------------#
@@ -442,10 +811,19 @@ module Pod
442
811
  link_with
443
812
  link_with_first_target
444
813
  inhibit_warnings
814
+ use_modular_headers
445
815
  user_project_path
446
816
  build_configurations
817
+ project_names
447
818
  dependencies
819
+ script_phases
448
820
  children
821
+ configuration_pod_whitelist
822
+ uses_frameworks
823
+ swift_version_requirements
824
+ inheritance
825
+ abstract
826
+ swift_version
449
827
  ).freeze
450
828
 
451
829
  # @return [Hash] The hash representation of the target definition.
@@ -477,7 +855,7 @@ module Pod
477
855
 
478
856
  #-----------------------------------------------------------------------#
479
857
 
480
- private
858
+ protected
481
859
 
482
860
  # @!group Private helpers
483
861
 
@@ -490,6 +868,8 @@ module Pod
490
868
  #
491
869
  attr_accessor :internal_hash
492
870
 
871
+ private
872
+
493
873
  # Set a value in the internal hash of the target definition for the given
494
874
  # key.
495
875
  #
@@ -499,7 +879,7 @@ module Pod
499
879
  # @param [Object] value
500
880
  # The value to store.
501
881
  #
502
- # @raise If the key is not recognized.
882
+ # @raise [StandardError] If the key is not recognized.
503
883
  #
504
884
  # @return [void]
505
885
  #
@@ -519,7 +899,7 @@ module Pod
519
899
  # @param [Object] base_value
520
900
  # The value to set if they key is nil. Useful for collections.
521
901
  #
522
- # @raise If the key is not recognized.
902
+ # @raise [StandardError] If the key is not recognized.
523
903
  #
524
904
  # @return [Object] The value for the key.
525
905
  #
@@ -527,16 +907,64 @@ module Pod
527
907
  unless HASH_KEYS.include?(key)
528
908
  raise StandardError, "Unsupported hash key `#{key}`"
529
909
  end
530
- internal_hash[key] ||= base_value
910
+ internal_hash[key] = base_value if internal_hash[key].nil?
911
+ internal_hash[key]
912
+ end
913
+
914
+ def raw_inhibit_warnings_hash
915
+ get_hash_value('inhibit_warnings', {})
531
916
  end
917
+ private :raw_inhibit_warnings_hash
532
918
 
533
919
  # Returns the inhibit_warnings hash pre-populated with default values.
534
920
  #
535
921
  # @return [Hash<String, Array>] Hash with :all key for inhibiting all
536
- # warnings, and :for_pods key for inhibiting warnings per Pod.
922
+ # warnings, :for_pods key for inhibiting warnings per Pod,
923
+ # and :not_for_pods key for not inhibiting warnings per Pod.
537
924
  #
538
925
  def inhibit_warnings_hash
539
- get_hash_value('inhibit_warnings', {})
926
+ inhibit_hash = raw_inhibit_warnings_hash
927
+ if exclusive?
928
+ inhibit_hash
929
+ else
930
+ parent_hash = parent.send(:inhibit_warnings_hash).dup
931
+ if parent_hash['not_for_pods']
932
+ # Remove pods that are set to not inhibit inside parent if they are set to inhibit inside current target.
933
+ parent_hash['not_for_pods'] -= Array(inhibit_hash['for_pods'])
934
+ end
935
+ if parent_hash['for_pods']
936
+ # Remove pods that are set to inhibit inside parent if they are set to not inhibit inside current target.
937
+ parent_hash['for_pods'] -= Array(inhibit_hash['for_pods'])
938
+ end
939
+ if inhibit_hash['all']
940
+ # Clean pods that are set to not inhibit inside parent if inhibit_all_warnings! was set.
941
+ parent_hash['not_for_pods'] = nil
942
+ inhibit_hash.delete('all') if parent_hash['all']
943
+ end
944
+ parent_hash.merge(inhibit_hash) do |_, l, r|
945
+ Array(l).concat(r).uniq
946
+ end
947
+ end
948
+ end
949
+
950
+ def raw_configuration_pod_whitelist
951
+ get_hash_value('configuration_pod_whitelist', {})
952
+ end
953
+ private :raw_configuration_pod_whitelist
954
+
955
+ # Returns the configuration_pod_whitelist hash
956
+ #
957
+ # @return [Hash<String, Array>] Hash with configuration name as key,
958
+ # array of pod names to be linked in builds with that configuration
959
+ # as value.
960
+ #
961
+ def configuration_pod_whitelist
962
+ whitelist_hash = raw_configuration_pod_whitelist
963
+ if exclusive?
964
+ whitelist_hash
965
+ else
966
+ parent.send(:configuration_pod_whitelist).merge(whitelist_hash) { |_, l, r| Array(l).concat(r).uniq }
967
+ end
540
968
  end
541
969
 
542
970
  # @return [Array<Dependency>] The dependencies specified by the user for
@@ -557,10 +985,10 @@ module Pod
557
985
 
558
986
  # @return [Array<Dependency>] The dependencies inherited by the podspecs.
559
987
  #
560
- # @note The podspec directive is intended include the dependencies of a
561
- # spec in the project where it is developed. For this reason the
988
+ # @note The podspec directive is intended to include the dependencies of
989
+ # a spec in the project where it is developed. For this reason the
562
990
  # spec, or any of it subspecs, cannot be included in the
563
- # dependencies. Otherwise it would generate an chicken-and-egg
991
+ # dependencies. Otherwise it would generate a chicken-and-egg
564
992
  # problem.
565
993
  #
566
994
  def podspec_dependencies
@@ -568,9 +996,18 @@ module Pod
568
996
  podspecs.map do |options|
569
997
  file = podspec_path_from_options(options)
570
998
  spec = Specification.from_file(file)
571
- all_specs = [spec, *spec.recursive_subspecs]
572
- all_deps = all_specs.map { |s| s.dependencies(platform) }.flatten
573
- all_deps.reject { |dep| dep.root_name == spec.root.name }
999
+ subspec_names = options[:subspecs] || options[:subspec]
1000
+ specs = if subspec_names.blank?
1001
+ [spec]
1002
+ else
1003
+ subspec_names = [subspec_names] if subspec_names.is_a?(String)
1004
+ subspec_names.map { |subspec_name| spec.subspec_by_name("#{spec.name}/#{subspec_name}") }
1005
+ end
1006
+ specs.map do |subspec|
1007
+ all_specs = [subspec, *subspec.recursive_subspecs]
1008
+ all_deps = all_specs.map { |s| s.dependencies(platform) }.flatten
1009
+ all_deps.reject { |dep| dep.root_name == subspec.root.name }
1010
+ end.flatten
574
1011
  end.flatten.uniq
575
1012
  end
576
1013
 
@@ -584,7 +1021,7 @@ module Pod
584
1021
  #
585
1022
  def podspec_path_from_options(options)
586
1023
  if path = options[:path]
587
- if File.extname(path) == '.podspec'
1024
+ if File.basename(path).include?('.podspec')
588
1025
  path_with_ext = path
589
1026
  else
590
1027
  path_with_ext = "#{path}.podspec"
@@ -592,18 +1029,24 @@ module Pod
592
1029
  path_without_tilde = path_with_ext.gsub('~', ENV['HOME'])
593
1030
  podfile.defined_in_file.dirname + path_without_tilde
594
1031
  elsif name = options[:name]
595
- name = File.extname(name) == '.podspec' ? name : "#{name}.podspec"
1032
+ name = File.basename(name).include?('.podspec') ? name : "#{name}.podspec"
596
1033
  podfile.defined_in_file.dirname + name
597
1034
  elsif options[:autodetect]
598
- glob_pattern = podfile.defined_in_file.dirname + '*.podspec'
599
- Pathname.glob(glob_pattern).first
1035
+ glob_pattern = podfile.defined_in_file.dirname + '*.podspec{,.json}'
1036
+ path = Pathname.glob(glob_pattern).first
1037
+ unless path
1038
+ raise Informative, 'Could not locate a podspec in the current directory. '\
1039
+ 'You can specify the path via the path option.'
1040
+ end
1041
+
1042
+ path
600
1043
  end
601
1044
  end
602
1045
 
603
1046
  # Removes :inhibit_warnings from the requirements list, and adds
604
1047
  # the pod's name into internal hash for disabling warnings.
605
1048
  #
606
- # @param [String] pod name
1049
+ # @param [String] name The name of the pod
607
1050
  #
608
1051
  # @param [Array] requirements
609
1052
  # If :inhibit_warnings is the only key in the hash, the hash
@@ -616,13 +1059,123 @@ module Pod
616
1059
  return requirements unless options.is_a?(Hash)
617
1060
 
618
1061
  should_inhibit = options.delete(:inhibit_warnings)
619
- inhibit_warnings_for_pod(name) if should_inhibit
1062
+ pod_name = Specification.root_name(name)
1063
+ set_inhibit_warnings_for_pod(pod_name, should_inhibit)
620
1064
 
621
1065
  requirements.pop if options.empty?
622
1066
  end
623
1067
 
624
- #-----------------------------------------------------------------------#
1068
+ # Removes :modular_headers from the requirements list, and adds
1069
+ # the pods name into internal hash for modular headers.
1070
+ #
1071
+ # @param [String] name The name of the pod
1072
+ #
1073
+ # @param [Array] requirements
1074
+ # If :modular_headers is the only key in the hash, the hash
1075
+ # should be destroyed because it confuses Gem::Dependency.
1076
+ #
1077
+ # @return [void]
1078
+ #
1079
+ def parse_modular_headers(name, requirements)
1080
+ options = requirements.last
1081
+ return requirements unless options.is_a?(Hash)
1082
+
1083
+ defines_module = options.delete(:modular_headers)
1084
+ pod_name = Specification.root_name(name)
1085
+ set_use_modular_headers_for_pod(pod_name, defines_module)
1086
+
1087
+ requirements.pop if options.empty?
1088
+ end
1089
+
1090
+ # Removes :project_name from the requirements list, and adds
1091
+ # the pods name into internal hash.
1092
+ #
1093
+ # @param [String] name The name of the pod
1094
+ #
1095
+ # @param [Array] requirements
1096
+ # If :project_name is the only key in the hash, the hash
1097
+ # should be destroyed because it confuses Gem::Dependency.
1098
+ #
1099
+ # @return [void]
1100
+ #
1101
+ def parse_project_name(name, requirements)
1102
+ options = requirements.last
1103
+ return requirements unless options.is_a?(Hash)
1104
+
1105
+ project_name = options.delete(:project_name)
1106
+ pod_name = Specification.root_name(name)
1107
+ raw_project_names_hash[pod_name] = project_name if project_name
625
1108
 
1109
+ requirements.pop if options.empty?
1110
+ end
1111
+
1112
+ def raw_project_names_hash
1113
+ get_hash_value('project_names', {})
1114
+ end
1115
+ private :raw_project_names_hash
1116
+
1117
+ # Removes :configurations or :configuration from the requirements list,
1118
+ # and adds the pod's name into the internal hash for which pods should be
1119
+ # linked in which configuration only.
1120
+ #
1121
+ # @param [String] name The name of the pod
1122
+ #
1123
+ # @param [Array] requirements
1124
+ # If :configurations is the only key in the hash, the hash
1125
+ # should be destroyed because it confuses Gem::Dependency.
1126
+ #
1127
+ # @return [void]
1128
+ #
1129
+ def parse_configuration_whitelist(name, requirements)
1130
+ options = requirements.last
1131
+ return requirements unless options.is_a?(Hash)
1132
+
1133
+ configurations = options.delete(:configurations)
1134
+ configurations ||= options.delete(:configuration)
1135
+ Array(configurations).each do |configuration|
1136
+ whitelist_pod_for_configuration(name, configuration)
1137
+ end
1138
+ requirements.pop if options.empty?
1139
+ end
1140
+
1141
+ # Removes :subspecs and :testspecs from the requirements list, and stores the pods
1142
+ # with the given subspecs or test specs as dependencies.
1143
+ #
1144
+ # @param [String] name
1145
+ #
1146
+ # @param [Array] requirements
1147
+ # If :subspecs is the only key in the hash, the hash
1148
+ # should be destroyed because it confuses Gem::Dependency.
1149
+ #
1150
+ # @return [Boolean] Whether new subspecs were added
1151
+ #
1152
+ def parse_subspecs(name, requirements)
1153
+ options = requirements.last
1154
+ return false unless options.is_a?(Hash)
1155
+
1156
+ subspecs = options.delete(:subspecs)
1157
+ test_specs = options.delete(:testspecs)
1158
+ app_specs = options.delete(:appspecs)
1159
+
1160
+ subspecs.each do |ss|
1161
+ store_pod("#{name}/#{ss}", *requirements.dup)
1162
+ end if subspecs
1163
+
1164
+ test_specs.each do |ss|
1165
+ requirements_copy = requirements.map(&:dup)
1166
+ store_pod("#{name}/#{ss}", *requirements_copy)
1167
+ end if test_specs
1168
+
1169
+ app_specs.each do |as|
1170
+ requirements_copy = requirements.map(&:dup)
1171
+ store_pod("#{name}/#{as}", *requirements_copy)
1172
+ end if app_specs
1173
+
1174
+ requirements.pop if options.empty?
1175
+ !subspecs.nil?
1176
+ end
1177
+
1178
+ #-----------------------------------------------------------------------#
626
1179
  end
627
1180
  end
628
1181
  end