cocoapods 0.16.4 → 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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +108 -0
  3. data/README.md +3 -3
  4. data/bin/pod +1 -1
  5. data/lib/cocoapods.rb +31 -31
  6. data/lib/cocoapods/command.rb +62 -107
  7. data/lib/cocoapods/command/inter_process_communication.rb +103 -0
  8. data/lib/cocoapods/command/list.rb +45 -44
  9. data/lib/cocoapods/command/outdated.rb +28 -25
  10. data/lib/cocoapods/command/project.rb +90 -0
  11. data/lib/cocoapods/command/push.rb +50 -32
  12. data/lib/cocoapods/command/repo.rb +125 -155
  13. data/lib/cocoapods/command/search.rb +23 -12
  14. data/lib/cocoapods/command/setup.rb +103 -64
  15. data/lib/cocoapods/command/spec.rb +329 -90
  16. data/lib/cocoapods/config.rb +197 -44
  17. data/lib/cocoapods/downloader.rb +47 -34
  18. data/lib/cocoapods/executable.rb +98 -41
  19. data/lib/cocoapods/external_sources.rb +325 -0
  20. data/lib/cocoapods/file_list.rb +8 -1
  21. data/lib/cocoapods/gem_version.rb +7 -0
  22. data/lib/cocoapods/generator/acknowledgements.rb +71 -7
  23. data/lib/cocoapods/generator/acknowledgements/markdown.rb +10 -9
  24. data/lib/cocoapods/generator/acknowledgements/plist.rb +9 -8
  25. data/lib/cocoapods/generator/copy_resources_script.rb +2 -2
  26. data/lib/cocoapods/generator/documentation.rb +153 -37
  27. data/lib/cocoapods/generator/prefix_header.rb +82 -0
  28. data/lib/cocoapods/generator/target_header.rb +58 -0
  29. data/lib/cocoapods/generator/xcconfig.rb +130 -0
  30. data/lib/cocoapods/hooks/installer_representation.rb +123 -0
  31. data/lib/cocoapods/hooks/library_representation.rb +79 -0
  32. data/lib/cocoapods/hooks/pod_representation.rb +74 -0
  33. data/lib/cocoapods/installer.rb +398 -147
  34. data/lib/cocoapods/installer/analyzer.rb +556 -0
  35. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +253 -0
  36. data/lib/cocoapods/installer/file_references_installer.rb +179 -0
  37. data/lib/cocoapods/installer/pod_source_installer.rb +289 -0
  38. data/lib/cocoapods/installer/target_installer.rb +307 -112
  39. data/lib/cocoapods/installer/user_project_integrator.rb +140 -176
  40. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +193 -0
  41. data/lib/cocoapods/library.rb +195 -0
  42. data/lib/cocoapods/open_uri.rb +16 -14
  43. data/lib/cocoapods/project.rb +175 -52
  44. data/lib/cocoapods/resolver.rb +151 -164
  45. data/lib/cocoapods/sandbox.rb +276 -54
  46. data/lib/cocoapods/sandbox/file_accessor.rb +210 -0
  47. data/lib/cocoapods/sandbox/headers_store.rb +96 -0
  48. data/lib/cocoapods/sandbox/path_list.rb +178 -0
  49. data/lib/cocoapods/sources_manager.rb +218 -0
  50. data/lib/cocoapods/user_interface.rb +82 -18
  51. data/lib/cocoapods/{command → user_interface}/error_report.rb +5 -5
  52. data/lib/cocoapods/validator.rb +379 -0
  53. metadata +74 -55
  54. data/lib/cocoapods/command/install.rb +0 -55
  55. data/lib/cocoapods/command/linter.rb +0 -317
  56. data/lib/cocoapods/command/update.rb +0 -25
  57. data/lib/cocoapods/dependency.rb +0 -285
  58. data/lib/cocoapods/downloader/git.rb +0 -276
  59. data/lib/cocoapods/downloader/http.rb +0 -99
  60. data/lib/cocoapods/downloader/mercurial.rb +0 -26
  61. data/lib/cocoapods/downloader/subversion.rb +0 -42
  62. data/lib/cocoapods/local_pod.rb +0 -620
  63. data/lib/cocoapods/lockfile.rb +0 -274
  64. data/lib/cocoapods/platform.rb +0 -127
  65. data/lib/cocoapods/podfile.rb +0 -551
  66. data/lib/cocoapods/source.rb +0 -223
  67. data/lib/cocoapods/specification.rb +0 -579
  68. data/lib/cocoapods/specification/set.rb +0 -175
  69. data/lib/cocoapods/specification/statistics.rb +0 -112
  70. data/lib/cocoapods/user_interface/ui_pod.rb +0 -130
  71. data/lib/cocoapods/version.rb +0 -26
@@ -0,0 +1,556 @@
1
+ module Pod
2
+ class Installer
3
+
4
+ # Analyzes the Podfile, the Lockfile, and the sandbox manifest to generate
5
+ # the information relative to a CocoaPods installation.
6
+ #
7
+ class Analyzer
8
+
9
+ include Config::Mixin
10
+
11
+ autoload :SandboxAnalyzer, 'cocoapods/installer/analyzer/sandbox_analyzer'
12
+
13
+ # @return [Sandbox] The sandbox where the Pods should be installed.
14
+ #
15
+ attr_reader :sandbox
16
+
17
+ # @return [Podfile] The Podfile specification that contains the
18
+ # information of the Pods that should be installed.
19
+ #
20
+ attr_reader :podfile
21
+
22
+ # @return [Lockfile] The Lockfile that stores the information about the
23
+ # Pods previously installed on any machine.
24
+ #
25
+ attr_reader :lockfile
26
+
27
+ # @param [Sandbox] sandbox @see sandbox
28
+ # @param [Podfile] podfile @see podfile
29
+ # @param [Lockfile] lockfile @see lockfile
30
+ #
31
+ def initialize(sandbox, podfile, lockfile = nil)
32
+ @sandbox = sandbox
33
+ @podfile = podfile
34
+ @lockfile = lockfile
35
+
36
+ @update_mode = false
37
+ @allow_pre_downloads = true
38
+ end
39
+
40
+ # Performs the analysis.
41
+ #
42
+ # The Podfile and the Lockfile provide the information necessary to
43
+ # compute which specification should be installed. The manifest of the
44
+ # sandbox returns which specifications are installed.
45
+ #
46
+ # @return [AnalysisResult]
47
+ #
48
+ def analyze
49
+ update_repositories_if_needed
50
+ @result = AnalysisResult.new
51
+ @result.podfile_state = generate_podfile_state
52
+ @locked_dependencies = generate_version_locking_dependencies
53
+
54
+ @result.libraries = generated_libraries
55
+ fetch_external_sources
56
+ @result.specs_by_target = resolve_dependencies
57
+ @result.specifications = generate_specifications
58
+ @result.sandbox_state = generate_sandbox_state
59
+ @result
60
+ end
61
+
62
+ attr_accessor :result
63
+
64
+ # @return [Bool] Whether an installation should be performed or this
65
+ # CocoaPods project is already up to date.
66
+ #
67
+ def needs_install?
68
+ podfile_needs_install? || sandbox_needs_install?
69
+ end
70
+
71
+ # @return [Bool] Whether the podfile has changes respect to the lockfile.
72
+ #
73
+ def podfile_needs_install?
74
+ state = generate_podfile_state
75
+ needing_install = state.added + state.changed + state.deleted
76
+ !needing_install.empty?
77
+ end
78
+
79
+ # @return [Bool] Whether the sandbox is in synch with the lockfile.
80
+ #
81
+ def sandbox_needs_install?
82
+ lockfile != sandbox.manifest
83
+ end
84
+
85
+ #-----------------------------------------------------------------------#
86
+
87
+ # @!group Configuration
88
+
89
+ # @return [Bool] Whether the version of the dependencies which did non
90
+ # change in the Podfile should be locked.
91
+ #
92
+ attr_accessor :update_mode
93
+ alias_method :update_mode?, :update_mode
94
+
95
+ # @return [Bool] Whether the analysis allows pre-downloads and thus
96
+ # modifications to the sandbox.
97
+ #
98
+ # @note This flag should not be used in installations.
99
+ #
100
+ # @note This is used by the `pod outdated` command to prevent
101
+ # modification of the sandbox in the resolution process.
102
+ #
103
+ attr_accessor :allow_pre_downloads
104
+ alias_method :allow_pre_downloads?, :allow_pre_downloads
105
+
106
+ #-----------------------------------------------------------------------#
107
+
108
+ private
109
+
110
+ # @!group Analysis steps
111
+
112
+ # Compares the {Podfile} with the {Lockfile} in order to detect which
113
+ # dependencies should be locked.
114
+ #
115
+ # @return [SpecsState] the states of the Podfile specs.
116
+ #
117
+ # @note As the target definitions share the same sandbox they should have
118
+ # the same version of a Pod. For this reason this method returns
119
+ # the name of the Pod (root name of the dependencies) and doesn't
120
+ # group them by target definition.
121
+ #
122
+ # @todo [CocoaPods > 0.18] If there isn't a Lockfile all the Pods should
123
+ # be marked as added.
124
+ #
125
+ def generate_podfile_state
126
+ if lockfile
127
+ pods_state = nil
128
+ UI.section "Finding Podfile changes" do
129
+ pods_by_state = lockfile.detect_changes_with_podfile(podfile)
130
+ pods_by_state.dup.each do |state, full_names|
131
+ pods_by_state[state] = full_names.map { |fn| Specification.root_name(fn) }
132
+ end
133
+ pods_state = SpecsState.new(pods_by_state)
134
+ pods_state.print
135
+ end
136
+ pods_state
137
+ else
138
+ state = SpecsState.new
139
+ state.added.concat(podfile.dependencies.map(&:root_name).uniq)
140
+ state
141
+ end
142
+ end
143
+
144
+ # Updates the source repositories unless the config indicates to skip it.
145
+ #
146
+ # @return [void]
147
+ #
148
+ def update_repositories_if_needed
149
+ unless config.skip_repo_update?
150
+ UI.section 'Updating spec repositories' do
151
+ SourcesManager.update
152
+ end
153
+ end
154
+ end
155
+
156
+ # Creates the models that represent the libraries generated by CocoaPods.
157
+ #
158
+ # @note The libraries are generated before the resolution process
159
+ # because it might be necessary to infer the platform from the
160
+ # user targets, which in turns requires to identify the user
161
+ # project.
162
+ #
163
+ # @note The specification of the libraries are added in the
164
+ # {#resolve_dependencies} step.
165
+ #
166
+ # @return [Array<Libraries>] the generated libraries.
167
+ #
168
+ def generated_libraries
169
+ libraries = []
170
+ podfile.target_definitions.values.each do |target_definition|
171
+ lib = Library.new(target_definition)
172
+ lib.support_files_root = sandbox.library_support_files_dir(lib.name)
173
+
174
+ if config.integrate_targets?
175
+ project_path = compute_user_project_path(target_definition)
176
+ user_project = Xcodeproj::Project.new(project_path)
177
+ targets = compute_user_project_targets(target_definition, user_project)
178
+
179
+ lib.user_project_path = project_path
180
+ lib.user_target_uuids = targets.map(&:uuid)
181
+ lib.user_build_configurations = compute_user_build_configurations(target_definition, targets)
182
+ lib.platform = compute_platform_for_target_definition(target_definition, targets)
183
+ else
184
+ unless target_definition.platform
185
+ raise Informative, "It is necessary to specify the platform in the Podfile if not integrating."
186
+ end
187
+ lib.user_project_path = config.installation_root
188
+ lib.user_target_uuids = []
189
+ lib.user_build_configurations = {}
190
+ lib.platform = target_definition.platform
191
+ end
192
+ libraries << lib
193
+ end
194
+ libraries
195
+ end
196
+
197
+ # Generates dependencies that require the specific version of the Pods
198
+ # that haven't changed in the {Lockfile}.
199
+ #
200
+ # These dependencies are passed to the {Resolver}, unless the installer
201
+ # is in update mode, to prevent it from upgrading the Pods that weren't
202
+ # changed in the {Podfile}.
203
+ #
204
+ # @return [Array<Dependency>] the dependencies generate by the lockfile
205
+ # that prevent the resolver to update a Pod.
206
+ #
207
+ def generate_version_locking_dependencies
208
+ if update_mode?
209
+ []
210
+ else
211
+ result.podfile_state.unchanged.map do |pod|
212
+ lockfile.dependency_to_lock_pod_named(pod)
213
+ end
214
+ end
215
+ end
216
+
217
+ # Fetches the podspecs of external sources if modifications to the
218
+ # sandbox are allowed.
219
+ #
220
+ # @note In update mode all the external sources are refreshed while in
221
+ # normal mode they are refreshed only if added or changed in the
222
+ # Podfile. Moreover, in normal specifications for unchanged Pods
223
+ # which are missing or are generated from an local source are
224
+ # fetched as well.
225
+ #
226
+ # @note It is possible to perform this step before the resolution
227
+ # process because external sources identify a single specific
228
+ # version (checkout). If the other dependencies are not
229
+ # compatible with the version reported by the podspec of the
230
+ # external source the resolver will raise.
231
+ #
232
+ # @return [void]
233
+ #
234
+ # TODO Specs
235
+ #
236
+ def fetch_external_sources
237
+ return unless allow_pre_downloads?
238
+ deps_to_fetch = []
239
+ deps_to_fetch_if_needed = []
240
+ deps_with_external_source = podfile.dependencies.select { |dep| dep.external_source }
241
+ if update_mode?
242
+ deps_to_fetch = deps_with_external_source
243
+ else
244
+ pods_to_fetch = result.podfile_state.added + result.podfile_state.changed
245
+ deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch.include?(dep.root_name) }
246
+ deps_to_fetch_if_needed = deps_with_external_source.select { |dep| result.podfile_state.unchanged.include?(dep.root_name) }
247
+ deps_to_fetch += deps_to_fetch_if_needed.select { |dep| sandbox.specification(dep.root_name).nil? || !dep.external_source[:local].nil? }
248
+ end
249
+
250
+ unless deps_to_fetch.empty?
251
+ UI.section "Fetching external sources" do
252
+ deps_to_fetch.uniq.sort.each do |dependency|
253
+ source = ExternalSources.from_dependency(dependency, podfile.defined_in_file)
254
+ source.fetch(sandbox)
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ # Converts the Podfile in a list of specifications grouped by target.
261
+ #
262
+ # @note In this step the specs are added to the libraries.
263
+ #
264
+ # @note As some dependencies might have external sources the resolver
265
+ # is aware of the {Sandbox} and interacts with it to download the
266
+ # podspecs of the external sources. This is necessary because the
267
+ # resolver needs their specifications to analyze their
268
+ # dependencies.
269
+ #
270
+ # @note The specifications of the external sources which are added,
271
+ # modified or removed need to deleted from the sandbox before the
272
+ # resolution process. Otherwise the resolver might use an
273
+ # incorrect specification instead of pre-downloading it.
274
+ #
275
+ # @note In update mode the resolver is set to always update the specs
276
+ # from external sources.
277
+ #
278
+ # @return [Hash{TargetDefinition => Array<Spec>}] the specifications
279
+ # grouped by target.
280
+ #
281
+ def resolve_dependencies
282
+ specs_by_target = nil
283
+
284
+ UI.section "Resolving dependencies of #{UI.path podfile.defined_in_file}" do
285
+ resolver = Resolver.new(sandbox, podfile, locked_dependencies)
286
+ specs_by_target = resolver.resolve
287
+ end
288
+
289
+ specs_by_target.each do |target_definition, specs|
290
+ lib = result.libraries.find { |l| l.target_definition == target_definition}
291
+ lib.specs = specs
292
+ end
293
+
294
+ specs_by_target
295
+ end
296
+
297
+ # Returns the list of all the resolved the resolved specifications.
298
+ #
299
+ # @return [Array<Specification>] the list of the specifications.
300
+ #
301
+ def generate_specifications
302
+ result.specs_by_target.values.flatten.uniq
303
+ end
304
+
305
+ # Computes the state of the sandbox respect to the resolved
306
+ # specifications.
307
+ #
308
+ # @return [SpecsState] the representation of the state of the manifest
309
+ # specifications.
310
+ #
311
+ def generate_sandbox_state
312
+ sandbox_state = nil
313
+ UI.section "Comparing resolved specification to the sandbox manifest" do
314
+ sandbox_analyzer = SandboxAnalyzer.new(sandbox, result.specifications, update_mode, lockfile)
315
+ sandbox_state = sandbox_analyzer.analyze
316
+ sandbox_state.print
317
+ end
318
+ sandbox_state
319
+ end
320
+
321
+ #-----------------------------------------------------------------------#
322
+
323
+ # @!group Analysis internal products
324
+
325
+ # @return [Array<Dependency>] the dependencies generate by the lockfile
326
+ # that prevent the resolver to update a Pod.
327
+ #
328
+ attr_reader :locked_dependencies
329
+
330
+ #-----------------------------------------------------------------------#
331
+
332
+ private
333
+
334
+ # @!group Analysis sub-steps
335
+
336
+ # Returns the path of the user project that the {TargetDefinition}
337
+ # should integrate.
338
+ #
339
+ # @raise If the project is implicit and there are multiple projects.
340
+ #
341
+ # @raise If the path doesn't exits.
342
+ #
343
+ # @return [Pathname] the path of the user project.
344
+ #
345
+ def compute_user_project_path(target_definition)
346
+ if target_definition.user_project_path
347
+ path = config.installation_root + target_definition.user_project_path
348
+ path = "#{path}.xcodeproj" unless File.extname(path) == '.xcodeproj'
349
+ path = Pathname.new(path)
350
+ unless path.exist?
351
+ raise Informative, "Unable to find the Xcode project " \
352
+ "`#{path}` for the target `#{target_definition.label}`."
353
+ end
354
+
355
+ else
356
+ xcodeprojs = Pathname.glob(config.installation_root + '*.xcodeproj')
357
+ if xcodeprojs.size == 1
358
+ path = xcodeprojs.first
359
+ else
360
+ raise Informative, "Could not automatically select an Xcode project. " \
361
+ "Specify one in your Podfile like so:\n\n" \
362
+ " xcodeproj 'path/to/Project.xcodeproj'\n"
363
+ end
364
+ end
365
+ path
366
+ end
367
+
368
+ # Returns a list of the targets from the project of {TargetDefinition}
369
+ # that needs to be integrated.
370
+ #
371
+ # @note The method first looks if there is a target specified with
372
+ # the `link_with` option of the {TargetDefinition}. Otherwise
373
+ # it looks for the target that has the same name of the target
374
+ # definition. Finally if no target was found the first
375
+ # encountered target is returned (it is assumed to be the one
376
+ # to integrate in simple projects).
377
+ #
378
+ # @note This will only return targets that do **not** already have
379
+ # the Pods library in their frameworks build phase.
380
+ #
381
+ #
382
+ def compute_user_project_targets(target_definition, user_project)
383
+ if link_with = target_definition.link_with
384
+ targets = native_targets(user_project).select { |t| link_with.include?(t.name) }
385
+ raise Informative, "Unable to find the targets named `#{link_with.to_sentence}` to link with target definition `#{target_definition.name}`" if targets.empty?
386
+ elsif target_definition.name == :default
387
+ targets = [ native_targets(user_project).first ].compact
388
+ raise Informative, "Unable to find a target" if targets.empty?
389
+ else
390
+ target = native_targets(user_project).find { |t| t.name == target_definition.name.to_s }
391
+ targets = [ target ].compact
392
+ raise Informative, "Unable to find a target named `#{target_definition.name.to_s}`" if targets.empty?
393
+ end
394
+ targets
395
+ end
396
+
397
+ # @return [Array<PBXNativeTarget>] Returns the user’s targets, excluding
398
+ # aggregate targets.
399
+ #
400
+ def native_targets(user_project)
401
+ user_project.targets.reject do |target|
402
+ target.is_a? Xcodeproj::Project::Object::PBXAggregateTarget
403
+ end
404
+ end
405
+
406
+ # @return [Hash{String=>Symbol}] A hash representing the user build
407
+ # configurations where each key corresponds to the name of a
408
+ # configuration and its value to its type (`:debug` or `:release`).
409
+ #
410
+ def compute_user_build_configurations(target_definition, user_targets)
411
+ if user_targets
412
+ user_targets.map { |t| t.build_configurations.map(&:name) }.flatten.inject({}) do |hash, name|
413
+ unless name == 'Debug' || name == 'Release'
414
+ hash[name] = :release
415
+ end
416
+ hash
417
+ end.merge(target_definition.build_configurations || {})
418
+ else
419
+ target_definition.build_configurations || {}
420
+ end
421
+ end
422
+
423
+ # @return [Platform] The platform for the library.
424
+ #
425
+ # @note This resolves to the lowest deployment target across the user
426
+ # targets.
427
+ #
428
+ # @todo Is assigning the platform to the target definition the best way
429
+ # to go?
430
+ #
431
+ def compute_platform_for_target_definition(target_definition, user_targets)
432
+ return target_definition.platform if target_definition.platform
433
+ name = nil
434
+ deployment_target = nil
435
+
436
+ user_targets.each do |target|
437
+ name ||= target.platform_name
438
+ raise Informative, "Targets with different platforms" unless name == target.platform_name
439
+ if !deployment_target || deployment_target > Version.new(target.deployment_target)
440
+ deployment_target = Version.new(target.deployment_target)
441
+ end
442
+ end
443
+
444
+ target_definition.set_platform(name, deployment_target)
445
+ platform = Platform.new(name, deployment_target)
446
+ platform
447
+ end
448
+
449
+ #-----------------------------------------------------------------------#
450
+
451
+ class AnalysisResult
452
+
453
+ # @return [SpecsState] the states of the Podfile specs.
454
+ #
455
+ attr_accessor :podfile_state
456
+
457
+ # @return [Hash{TargetDefinition => Array<Spec>}] the specifications
458
+ # grouped by target.
459
+ #
460
+ attr_accessor :specs_by_target
461
+
462
+ # @return [Array<Specification>] the specifications of the resolved
463
+ # version of Pods that should be installed.
464
+ #
465
+ attr_accessor :specifications
466
+
467
+ # @return [SpecsState] the states of the {Sandbox} respect the resolved
468
+ # specifications.
469
+ #
470
+ attr_accessor :sandbox_state
471
+
472
+ # @return [Array<Library>] the libraries generated by the target
473
+ # definitions.
474
+ #
475
+ attr_accessor :libraries
476
+
477
+ end
478
+
479
+ #-----------------------------------------------------------------------#
480
+
481
+ # This class represents the state of a collection of Pods.
482
+ #
483
+ # @note The names of the pods stored by this class are always the **root**
484
+ # name of the specification.
485
+ #
486
+ # @note The motivation for this class is to ensure that the names of the
487
+ # subspecs are added instead of the name of the Pods.
488
+ #
489
+ class SpecsState
490
+
491
+ # @param [Hash{Symbol=>String}] pods_by_state
492
+ # The **root** name of the pods grouped by their state
493
+ # (`:added`, `:removed`, `:changed` or `:unchanged`).
494
+ #
495
+ def initialize(pods_by_state = nil)
496
+ @added = []
497
+ @deleted = []
498
+ @changed = []
499
+ @unchanged = []
500
+
501
+ if pods_by_state
502
+ @added = pods_by_state[:added] || []
503
+ @deleted = pods_by_state[:removed] || []
504
+ @changed = pods_by_state[:changed] || []
505
+ @unchanged = pods_by_state[:unchanged] || []
506
+ end
507
+ end
508
+
509
+ # @return [Array<String>] the names of the pods that were added.
510
+ #
511
+ attr_accessor :added
512
+
513
+ # @return [Array<String>] the names of the pods that were changed.
514
+ #
515
+ attr_accessor :changed
516
+
517
+ # @return [Array<String>] the names of the pods that were deleted.
518
+ #
519
+ attr_accessor :deleted
520
+
521
+ # @return [Array<String>] the names of the pods that were unchanged.
522
+ #
523
+ attr_accessor :unchanged
524
+
525
+ # Displays the state of each pod.
526
+ #
527
+ # @return [void]
528
+ #
529
+ def print
530
+ added .sort.each { |pod| UI.message("A".green + " #{pod}", '', 2) }
531
+ deleted .sort.each { |pod| UI.message("R".red + " #{pod}", '', 2) }
532
+ changed .sort.each { |pod| UI.message("M".yellow + " #{pod}", '', 2) }
533
+ unchanged.sort.each { |pod| UI.message("-" + " #{pod}", '', 2) }
534
+ end
535
+
536
+ # Adds the name of a Pod to the give state.
537
+ #
538
+ # @param [String]
539
+ # the name of the Pod.
540
+ #
541
+ # @param [Symbol]
542
+ # the state of the Pod.
543
+ #
544
+ # @raise If there is an attempt to add the name of a subspec.
545
+ #
546
+ # @return [void]
547
+ #
548
+ def add_name(name, state)
549
+ raise "[Bug] Attempt to add subspec to the pods state" if name.include?('/')
550
+ self.send(state) << name
551
+ end
552
+
553
+ end
554
+ end
555
+ end
556
+ end