xcocoapods 1.5.3

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 (124) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6303 -0
  3. data/LICENSE +28 -0
  4. data/README.md +80 -0
  5. data/bin/pod +56 -0
  6. data/bin/sandbox-pod +168 -0
  7. data/lib/cocoapods.rb +73 -0
  8. data/lib/cocoapods/command.rb +175 -0
  9. data/lib/cocoapods/command/cache.rb +28 -0
  10. data/lib/cocoapods/command/cache/clean.rb +90 -0
  11. data/lib/cocoapods/command/cache/list.rb +69 -0
  12. data/lib/cocoapods/command/env.rb +66 -0
  13. data/lib/cocoapods/command/init.rb +128 -0
  14. data/lib/cocoapods/command/install.rb +45 -0
  15. data/lib/cocoapods/command/ipc.rb +19 -0
  16. data/lib/cocoapods/command/ipc/list.rb +40 -0
  17. data/lib/cocoapods/command/ipc/podfile.rb +31 -0
  18. data/lib/cocoapods/command/ipc/podfile_json.rb +30 -0
  19. data/lib/cocoapods/command/ipc/repl.rb +51 -0
  20. data/lib/cocoapods/command/ipc/spec.rb +29 -0
  21. data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
  22. data/lib/cocoapods/command/lib.rb +11 -0
  23. data/lib/cocoapods/command/lib/create.rb +105 -0
  24. data/lib/cocoapods/command/lib/lint.rb +121 -0
  25. data/lib/cocoapods/command/list.rb +39 -0
  26. data/lib/cocoapods/command/options/project_directory.rb +36 -0
  27. data/lib/cocoapods/command/options/repo_update.rb +34 -0
  28. data/lib/cocoapods/command/outdated.rb +140 -0
  29. data/lib/cocoapods/command/repo.rb +29 -0
  30. data/lib/cocoapods/command/repo/add.rb +103 -0
  31. data/lib/cocoapods/command/repo/lint.rb +82 -0
  32. data/lib/cocoapods/command/repo/list.rb +93 -0
  33. data/lib/cocoapods/command/repo/push.rb +281 -0
  34. data/lib/cocoapods/command/repo/remove.rb +36 -0
  35. data/lib/cocoapods/command/repo/update.rb +28 -0
  36. data/lib/cocoapods/command/setup.rb +103 -0
  37. data/lib/cocoapods/command/spec.rb +112 -0
  38. data/lib/cocoapods/command/spec/cat.rb +51 -0
  39. data/lib/cocoapods/command/spec/create.rb +283 -0
  40. data/lib/cocoapods/command/spec/edit.rb +87 -0
  41. data/lib/cocoapods/command/spec/env_spec.rb +53 -0
  42. data/lib/cocoapods/command/spec/lint.rb +137 -0
  43. data/lib/cocoapods/command/spec/which.rb +43 -0
  44. data/lib/cocoapods/command/update.rb +101 -0
  45. data/lib/cocoapods/config.rb +347 -0
  46. data/lib/cocoapods/core_overrides.rb +1 -0
  47. data/lib/cocoapods/downloader.rb +190 -0
  48. data/lib/cocoapods/downloader/cache.rb +233 -0
  49. data/lib/cocoapods/downloader/request.rb +86 -0
  50. data/lib/cocoapods/downloader/response.rb +16 -0
  51. data/lib/cocoapods/executable.rb +222 -0
  52. data/lib/cocoapods/external_sources.rb +57 -0
  53. data/lib/cocoapods/external_sources/abstract_external_source.rb +205 -0
  54. data/lib/cocoapods/external_sources/downloader_source.rb +30 -0
  55. data/lib/cocoapods/external_sources/path_source.rb +55 -0
  56. data/lib/cocoapods/external_sources/podspec_source.rb +54 -0
  57. data/lib/cocoapods/gem_version.rb +5 -0
  58. data/lib/cocoapods/generator/acknowledgements.rb +107 -0
  59. data/lib/cocoapods/generator/acknowledgements/markdown.rb +44 -0
  60. data/lib/cocoapods/generator/acknowledgements/plist.rb +94 -0
  61. data/lib/cocoapods/generator/app_target_helper.rb +244 -0
  62. data/lib/cocoapods/generator/bridge_support.rb +22 -0
  63. data/lib/cocoapods/generator/constant.rb +19 -0
  64. data/lib/cocoapods/generator/copy_resources_script.rb +230 -0
  65. data/lib/cocoapods/generator/dummy_source.rb +31 -0
  66. data/lib/cocoapods/generator/embed_frameworks_script.rb +215 -0
  67. data/lib/cocoapods/generator/header.rb +103 -0
  68. data/lib/cocoapods/generator/info_plist_file.rb +116 -0
  69. data/lib/cocoapods/generator/module_map.rb +99 -0
  70. data/lib/cocoapods/generator/prefix_header.rb +60 -0
  71. data/lib/cocoapods/generator/umbrella_header.rb +46 -0
  72. data/lib/cocoapods/hooks_manager.rb +132 -0
  73. data/lib/cocoapods/installer.rb +703 -0
  74. data/lib/cocoapods/installer/analyzer.rb +972 -0
  75. data/lib/cocoapods/installer/analyzer/analysis_result.rb +87 -0
  76. data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +98 -0
  77. data/lib/cocoapods/installer/analyzer/pod_variant.rb +67 -0
  78. data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +157 -0
  79. data/lib/cocoapods/installer/analyzer/podfile_dependency_cache.rb +54 -0
  80. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +240 -0
  81. data/lib/cocoapods/installer/analyzer/specs_state.rb +84 -0
  82. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +53 -0
  83. data/lib/cocoapods/installer/analyzer/target_inspector.rb +260 -0
  84. data/lib/cocoapods/installer/installation_options.rb +158 -0
  85. data/lib/cocoapods/installer/pod_source_installer.rb +202 -0
  86. data/lib/cocoapods/installer/pod_source_preparer.rb +77 -0
  87. data/lib/cocoapods/installer/podfile_validator.rb +139 -0
  88. data/lib/cocoapods/installer/post_install_hooks_context.rb +132 -0
  89. data/lib/cocoapods/installer/pre_install_hooks_context.rb +51 -0
  90. data/lib/cocoapods/installer/source_provider_hooks_context.rb +34 -0
  91. data/lib/cocoapods/installer/user_project_integrator.rb +250 -0
  92. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +463 -0
  93. data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
  94. data/lib/cocoapods/installer/xcode.rb +8 -0
  95. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +416 -0
  96. data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +181 -0
  97. data/lib/cocoapods/installer/xcode/pods_project_generator/app_host_installer.rb +84 -0
  98. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +334 -0
  99. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +777 -0
  100. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +116 -0
  101. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installation_result.rb +86 -0
  102. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +256 -0
  103. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer_helper.rb +68 -0
  104. data/lib/cocoapods/installer/xcode/target_validator.rb +147 -0
  105. data/lib/cocoapods/open-uri.rb +33 -0
  106. data/lib/cocoapods/project.rb +414 -0
  107. data/lib/cocoapods/resolver.rb +585 -0
  108. data/lib/cocoapods/resolver/lazy_specification.rb +79 -0
  109. data/lib/cocoapods/sandbox.rb +404 -0
  110. data/lib/cocoapods/sandbox/file_accessor.rb +444 -0
  111. data/lib/cocoapods/sandbox/headers_store.rb +146 -0
  112. data/lib/cocoapods/sandbox/path_list.rb +220 -0
  113. data/lib/cocoapods/sandbox/pod_dir_cleaner.rb +85 -0
  114. data/lib/cocoapods/sandbox/podspec_finder.rb +23 -0
  115. data/lib/cocoapods/sources_manager.rb +157 -0
  116. data/lib/cocoapods/target.rb +261 -0
  117. data/lib/cocoapods/target/aggregate_target.rb +338 -0
  118. data/lib/cocoapods/target/build_settings.rb +1075 -0
  119. data/lib/cocoapods/target/pod_target.rb +559 -0
  120. data/lib/cocoapods/user_interface.rb +459 -0
  121. data/lib/cocoapods/user_interface/error_report.rb +187 -0
  122. data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
  123. data/lib/cocoapods/validator.rb +981 -0
  124. metadata +533 -0
@@ -0,0 +1,703 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'fileutils'
3
+
4
+ module Pod
5
+ # The Installer is responsible of taking a Podfile and transform it in the
6
+ # Pods libraries. It also integrates the user project so the Pods
7
+ # libraries can be used out of the box.
8
+ #
9
+ # The Installer is capable of doing incremental updates to an existing Pod
10
+ # installation.
11
+ #
12
+ # The Installer gets the information that it needs mainly from 3 files:
13
+ #
14
+ # - Podfile: The specification written by the user that contains
15
+ # information about targets and Pods.
16
+ # - Podfile.lock: Contains information about the pods that were previously
17
+ # installed and in concert with the Podfile provides information about
18
+ # which specific version of a Pod should be installed. This file is
19
+ # ignored in update mode.
20
+ # - Manifest.lock: A file contained in the Pods folder that keeps track of
21
+ # the pods installed in the local machine. This files is used once the
22
+ # exact versions of the Pods has been computed to detect if that version
23
+ # is already installed. This file is not intended to be kept under source
24
+ # control and is a copy of the Podfile.lock.
25
+ #
26
+ # The Installer is designed to work in environments where the Podfile folder
27
+ # is under source control and environments where it is not. The rest of the
28
+ # files, like the user project and the workspace are assumed to be under
29
+ # source control.
30
+ #
31
+ class Installer
32
+ autoload :Analyzer, 'cocoapods/installer/analyzer'
33
+ autoload :InstallationOptions, 'cocoapods/installer/installation_options'
34
+ autoload :PostInstallHooksContext, 'cocoapods/installer/post_install_hooks_context'
35
+ autoload :PreInstallHooksContext, 'cocoapods/installer/pre_install_hooks_context'
36
+ autoload :SourceProviderHooksContext, 'cocoapods/installer/source_provider_hooks_context'
37
+ autoload :PodfileValidator, 'cocoapods/installer/podfile_validator'
38
+ autoload :PodSourceInstaller, 'cocoapods/installer/pod_source_installer'
39
+ autoload :PodSourcePreparer, 'cocoapods/installer/pod_source_preparer'
40
+ autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator'
41
+ autoload :Xcode, 'cocoapods/installer/xcode'
42
+
43
+ include Config::Mixin
44
+ include InstallationOptions::Mixin
45
+
46
+ delegate_installation_options { podfile }
47
+
48
+ # @return [Sandbox] The sandbox where the Pods should be installed.
49
+ #
50
+ attr_reader :sandbox
51
+
52
+ # @return [Podfile] The Podfile specification that contains the information
53
+ # of the Pods that should be installed.
54
+ #
55
+ attr_reader :podfile
56
+
57
+ # @return [Lockfile] The Lockfile that stores the information about the
58
+ # Pods previously installed on any machine.
59
+ #
60
+ attr_reader :lockfile
61
+
62
+ # Initialize a new instance
63
+ #
64
+ # @param [Sandbox] sandbox @see #sandbox
65
+ # @param [Podfile] podfile @see #podfile
66
+ # @param [Lockfile] lockfile @see #lockfile
67
+ #
68
+ def initialize(sandbox, podfile, lockfile = nil)
69
+ @sandbox = sandbox || raise(ArgumentError, 'Missing required argument `sandbox`')
70
+ @podfile = podfile || raise(ArgumentError, 'Missing required argument `podfile`')
71
+ @lockfile = lockfile
72
+
73
+ @use_default_plugins = true
74
+ @has_dependencies = true
75
+ end
76
+
77
+ # @return [Hash, Boolean, nil] Pods that have been requested to be
78
+ # updated or true if all Pods should be updated.
79
+ # If all Pods should been updated the contents of the Lockfile are
80
+ # not taken into account for deciding what Pods to install.
81
+ #
82
+ attr_accessor :update
83
+
84
+ # @return [Boolean] Whether it has dependencies. Defaults to true.
85
+ #
86
+ attr_accessor :has_dependencies
87
+ alias_method :has_dependencies?, :has_dependencies
88
+
89
+ # @return [Boolean] Whether the spec repos should be updated.
90
+ #
91
+ attr_accessor :repo_update
92
+ alias_method :repo_update?, :repo_update
93
+
94
+ # @return [Boolean] Whether default plugins should be used during
95
+ # installation. Defaults to true.
96
+ #
97
+ attr_accessor :use_default_plugins
98
+ alias_method :use_default_plugins?, :use_default_plugins
99
+
100
+ # Installs the Pods.
101
+ #
102
+ # The installation process is mostly linear with a few minor complications
103
+ # to keep in mind:
104
+ #
105
+ # - The stored podspecs need to be cleaned before the resolution step
106
+ # otherwise the sandbox might return an old podspec and not download
107
+ # the new one from an external source.
108
+ # - The resolver might trigger the download of Pods from external sources
109
+ # necessary to retrieve their podspec (unless it is instructed not to
110
+ # do it).
111
+ #
112
+ # @return [void]
113
+ #
114
+ def install!
115
+ prepare
116
+ resolve_dependencies
117
+ download_dependencies
118
+ validate_targets
119
+ generate_pods_project
120
+ if installation_options.integrate_targets?
121
+ integrate_user_project
122
+ else
123
+ UI.section 'Skipping User Project Integration'
124
+ end
125
+ perform_post_install_actions
126
+ end
127
+
128
+ def prepare
129
+ # Raise if pwd is inside Pods
130
+ if Dir.pwd.start_with?(sandbox.root.to_path)
131
+ message = 'Command should be run from a directory outside Pods directory.'
132
+ message << "\n\n\tCurrent directory is #{UI.path(Pathname.pwd)}\n"
133
+ raise Informative, message
134
+ end
135
+ UI.message 'Preparing' do
136
+ deintegrate_if_different_major_version
137
+ sandbox.prepare
138
+ ensure_plugins_are_installed!
139
+ run_plugins_pre_install_hooks
140
+ end
141
+ end
142
+
143
+ # @return [Analyzer] The analyzer used to resolve dependencies
144
+ #
145
+ def resolve_dependencies
146
+ plugin_sources = run_source_provider_hooks
147
+ analyzer = create_analyzer(plugin_sources)
148
+
149
+ UI.section 'Updating local specs repositories' do
150
+ analyzer.update_repositories
151
+ end if repo_update?
152
+
153
+ UI.section 'Analyzing dependencies' do
154
+ analyze(analyzer)
155
+ validate_build_configurations
156
+ clean_sandbox
157
+ end
158
+ analyzer
159
+ end
160
+
161
+ def download_dependencies
162
+ UI.section 'Downloading dependencies' do
163
+ install_pod_sources
164
+ run_podfile_pre_install_hooks
165
+ clean_pod_sources
166
+ end
167
+ end
168
+
169
+ #-------------------------------------------------------------------------#
170
+
171
+ # @!group Pods Project Generation
172
+
173
+ private
174
+
175
+ def create_generator
176
+ Xcode::PodsProjectGenerator.new(sandbox, aggregate_targets, pod_targets, analysis_result, installation_options, config)
177
+ end
178
+
179
+ # Generate the 'Pods/Pods.xcodeproj' project.
180
+ #
181
+ def generate_pods_project(generator = create_generator)
182
+ UI.section 'Generating Pods project' do
183
+ @target_installation_results = generator.generate!
184
+ @pods_project = generator.project
185
+ run_podfile_post_install_hooks
186
+ generator.write
187
+ generator.share_development_pod_schemes
188
+ write_lockfiles
189
+ end
190
+ end
191
+
192
+ #-------------------------------------------------------------------------#
193
+
194
+ public
195
+
196
+ # @!group Installation results
197
+
198
+ # @return [Analyzer] the analyzer which provides the information about what
199
+ # needs to be installed.
200
+ #
201
+ attr_reader :analysis_result
202
+
203
+ # @return [Array<Hash{String, TargetInstallationResult}>] the installation results produced by the pods project
204
+ # generator
205
+ #
206
+ attr_reader :target_installation_results
207
+
208
+ # @return [Pod::Project] the `Pods/Pods.xcodeproj` project.
209
+ #
210
+ attr_reader :pods_project
211
+
212
+ # @return [Array<AggregateTarget>] The model representations of an
213
+ # aggregation of pod targets generated for a target definition
214
+ # in the Podfile as result of the analyzer.
215
+ #
216
+ attr_reader :aggregate_targets
217
+
218
+ # @return [Array<PodTarget>] The model representations of pod targets
219
+ # generated as result of the analyzer.
220
+ #
221
+ attr_reader :pod_targets
222
+
223
+ # @return [Array<Specification>] The specifications that were installed.
224
+ #
225
+ attr_accessor :installed_specs
226
+
227
+ #-------------------------------------------------------------------------#
228
+
229
+ private
230
+
231
+ # @!group Installation steps
232
+
233
+ # Performs the analysis.
234
+ #
235
+ # @return [void]
236
+ #
237
+ def analyze(analyzer = create_analyzer)
238
+ @analysis_result = analyzer.analyze
239
+ @aggregate_targets = @analysis_result.targets
240
+ @pod_targets = @analysis_result.pod_targets
241
+ end
242
+
243
+ def create_analyzer(plugin_sources = nil)
244
+ Analyzer.new(sandbox, podfile, lockfile, plugin_sources, has_dependencies?, update).tap do |analyzer|
245
+ analyzer.installation_options = installation_options
246
+ end
247
+ end
248
+
249
+ # Ensures that the white-listed build configurations are known to prevent
250
+ # silent typos.
251
+ #
252
+ # @raise If an unknown user configuration is found.
253
+ #
254
+ def validate_build_configurations
255
+ whitelisted_configs = pod_targets.
256
+ flat_map(&:target_definitions).
257
+ flat_map(&:all_whitelisted_configurations).
258
+ map(&:downcase).
259
+ uniq
260
+ all_user_configurations = analysis_result.all_user_build_configurations.keys.map(&:downcase)
261
+
262
+ remainder = whitelisted_configs - all_user_configurations
263
+ unless remainder.empty?
264
+ raise Informative,
265
+ "Unknown #{'configuration'.pluralize(remainder.size)} whitelisted: #{remainder.sort.to_sentence}. " \
266
+ "CocoaPods found #{all_user_configurations.sort.to_sentence}, did you mean one of these?"
267
+ end
268
+ end
269
+
270
+ # @return [void] In this step we clean all the folders that will be
271
+ # regenerated from scratch and any file which might not be
272
+ # overwritten.
273
+ #
274
+ # @todo [#247] Clean the headers of only the pods to install.
275
+ #
276
+ def clean_sandbox
277
+ sandbox.public_headers.implode!
278
+ target_support_dirs = sandbox.target_support_files_root.children.select(&:directory?)
279
+ pod_targets.each do |pod_target|
280
+ pod_target.build_headers.implode!
281
+ target_support_dirs.delete(pod_target.support_files_dir)
282
+ end
283
+
284
+ aggregate_targets.each do |aggregate_target|
285
+ target_support_dirs.delete(aggregate_target.support_files_dir)
286
+ end
287
+
288
+ target_support_dirs.each { |dir| FileUtils.rm_rf(dir) }
289
+
290
+ unless sandbox_state.deleted.empty?
291
+ title_options = { :verbose_prefix => '-> '.red }
292
+ sandbox_state.deleted.each do |pod_name|
293
+ UI.titled_section("Removing #{pod_name}".red, title_options) do
294
+ sandbox.clean_pod(pod_name)
295
+ end
296
+ end
297
+ end
298
+ end
299
+
300
+ # Downloads, installs the documentation and cleans the sources of the Pods
301
+ # which need to be installed.
302
+ #
303
+ # @return [void]
304
+ #
305
+ def install_pod_sources
306
+ @installed_specs = []
307
+ pods_to_install = sandbox_state.added | sandbox_state.changed
308
+ title_options = { :verbose_prefix => '-> '.green }
309
+ root_specs.sort_by(&:name).each do |spec|
310
+ if pods_to_install.include?(spec.name)
311
+ if sandbox_state.changed.include?(spec.name) && sandbox.manifest
312
+ current_version = spec.version
313
+ previous_version = sandbox.manifest.version(spec.name)
314
+ has_changed_version = current_version != previous_version
315
+ current_repo = analysis_result.specs_by_source.detect { |key, values| break key if values.map(&:name).include?(spec.name) }
316
+ current_repo &&= current_repo.url || current_repo.name
317
+ previous_spec_repo = sandbox.manifest.spec_repo(spec.name)
318
+ has_changed_repo = !previous_spec_repo.nil? && current_repo && !current_repo.casecmp(previous_spec_repo).zero?
319
+ title = "Installing #{spec.name} #{spec.version}"
320
+ title << " (was #{previous_version} and source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if has_changed_version && has_changed_repo
321
+ title << " (was #{previous_version})" if has_changed_version && !has_changed_repo
322
+ title << " (source changed to `#{current_repo}` from `#{previous_spec_repo}`)" if !has_changed_version && has_changed_repo
323
+ else
324
+ title = "Installing #{spec}"
325
+ end
326
+ UI.titled_section(title.green, title_options) do
327
+ install_source_of_pod(spec.name)
328
+ end
329
+ else
330
+ UI.titled_section("Using #{spec}", title_options) do
331
+ create_pod_installer(spec.name)
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ def create_pod_installer(pod_name)
338
+ specs_by_platform = {}
339
+ pod_targets.each do |pod_target|
340
+ if pod_target.root_spec.name == pod_name
341
+ specs_by_platform[pod_target.platform] ||= []
342
+ specs_by_platform[pod_target.platform].concat(pod_target.specs)
343
+ end
344
+ end
345
+
346
+ raise Informative, "Could not install '#{pod_name}' pod. There is no target that supports it." if specs_by_platform.empty?
347
+
348
+ @pod_installers ||= []
349
+ pod_installer = PodSourceInstaller.new(sandbox, specs_by_platform, :can_cache => installation_options.clean?)
350
+ @pod_installers << pod_installer
351
+ pod_installer
352
+ end
353
+
354
+ # Install the Pods. If the resolver indicated that a Pod should be
355
+ # installed and it exits, it is removed and then reinstalled. In any case if
356
+ # the Pod doesn't exits it is installed.
357
+ #
358
+ # @return [void]
359
+ #
360
+ def install_source_of_pod(pod_name)
361
+ pod_installer = create_pod_installer(pod_name)
362
+ pod_installer.install!
363
+ @installed_specs.concat(pod_installer.specs_by_platform.values.flatten.uniq)
364
+ end
365
+
366
+ # Cleans the sources of the Pods if the config instructs to do so.
367
+ #
368
+ # @todo Why the @pod_installers might be empty?
369
+ #
370
+ def clean_pod_sources
371
+ return unless installation_options.clean?
372
+ return unless @pod_installers
373
+ @pod_installers.each(&:clean!)
374
+ end
375
+
376
+ # Unlocks the sources of the Pods.
377
+ #
378
+ # @todo Why the @pod_installers might be empty?
379
+ #
380
+ def unlock_pod_sources
381
+ return unless @pod_installers
382
+ @pod_installers.each do |installer|
383
+ pod_target = pod_targets.find { |target| target.pod_name == installer.name }
384
+ installer.unlock_files!(pod_target.file_accessors)
385
+ end
386
+ end
387
+
388
+ # Locks the sources of the Pods if the config instructs to do so.
389
+ #
390
+ # @todo Why the @pod_installers might be empty?
391
+ #
392
+ def lock_pod_sources
393
+ return unless installation_options.lock_pod_sources?
394
+ return unless @pod_installers
395
+ @pod_installers.each do |installer|
396
+ pod_target = pod_targets.find { |target| target.pod_name == installer.name }
397
+ installer.lock_files!(pod_target.file_accessors)
398
+ end
399
+ end
400
+
401
+ def validate_targets
402
+ validator = Xcode::TargetValidator.new(aggregate_targets, pod_targets)
403
+ validator.validate!
404
+ end
405
+
406
+ # Runs the registered callbacks for the plugins pre install hooks.
407
+ #
408
+ # @return [void]
409
+ #
410
+ def run_plugins_pre_install_hooks
411
+ context = PreInstallHooksContext.generate(sandbox, podfile, lockfile)
412
+ HooksManager.run(:pre_install, context, plugins)
413
+ end
414
+
415
+ # Performs any post-installation actions
416
+ #
417
+ # @return [void]
418
+ #
419
+ def perform_post_install_actions
420
+ run_plugins_post_install_hooks
421
+ warn_for_deprecations
422
+ warn_for_installed_script_phases
423
+ print_post_install_message
424
+ end
425
+
426
+ def print_post_install_message
427
+ podfile_dependencies = analysis_result.podfile_dependency_cache.podfile_dependencies.size
428
+ pods_installed = root_specs.size
429
+ title_options = { :verbose_prefix => '-> '.green }
430
+ UI.titled_section('Pod installation complete! ' \
431
+ "There #{podfile_dependencies == 1 ? 'is' : 'are'} #{podfile_dependencies} " \
432
+ "#{'dependency'.pluralize(podfile_dependencies)} from the Podfile " \
433
+ "and #{pods_installed} total #{'pod'.pluralize(pods_installed)} installed.".green,
434
+ title_options)
435
+ end
436
+
437
+ # Runs the registered callbacks for the plugins post install hooks.
438
+ #
439
+ def run_plugins_post_install_hooks
440
+ # This short-circuits because unlocking pod sources is expensive
441
+ if any_plugin_post_install_hooks?
442
+ unlock_pod_sources
443
+
444
+ context = PostInstallHooksContext.generate(sandbox, aggregate_targets)
445
+ HooksManager.run(:post_install, context, plugins)
446
+ end
447
+
448
+ lock_pod_sources
449
+ end
450
+
451
+ # @return [Boolean] whether there are any plugin post-install hooks to run
452
+ #
453
+ def any_plugin_post_install_hooks?
454
+ HooksManager.hooks_to_run(:post_install, plugins).any?
455
+ end
456
+
457
+ # Runs the registered callbacks for the source provider plugin hooks.
458
+ #
459
+ # @return [void]
460
+ #
461
+ def run_source_provider_hooks
462
+ context = SourceProviderHooksContext.generate
463
+ HooksManager.run(:source_provider, context, plugins)
464
+ context.sources
465
+ end
466
+
467
+ # Run the deintegrator against all projects in the installation root if the
468
+ # current CocoaPods major version part is different than the one in the
469
+ # lockfile.
470
+ #
471
+ # @return [void]
472
+ #
473
+ def deintegrate_if_different_major_version
474
+ return unless lockfile
475
+ return if lockfile.cocoapods_version.major == Version.create(VERSION).major
476
+ UI.section('Re-creating CocoaPods due to major version update.') do
477
+ projects = Pathname.glob(config.installation_root + '*.xcodeproj').map { |path| Xcodeproj::Project.open(path) }
478
+ deintegrator = Deintegrator.new
479
+ projects.each do |project|
480
+ config.with_changes(:silent => true) { deintegrator.deintegrate_project(project) }
481
+ project.save if project.dirty?
482
+ end
483
+ end
484
+ end
485
+
486
+ # Ensures that all plugins specified in the {#podfile} are loaded.
487
+ #
488
+ # @return [void]
489
+ #
490
+ def ensure_plugins_are_installed!
491
+ require 'claide/command/plugin_manager'
492
+
493
+ loaded_plugins = Command::PluginManager.specifications.map(&:name)
494
+
495
+ podfile.plugins.keys.each do |plugin|
496
+ unless loaded_plugins.include? plugin
497
+ raise Informative, "Your Podfile requires that the plugin `#{plugin}` be installed. Please install it and try installation again."
498
+ end
499
+ end
500
+ end
501
+
502
+ DEFAULT_PLUGINS = { 'cocoapods-stats' => {} }
503
+
504
+ # Returns the plugins that should be run, as indicated by the default
505
+ # plugins and the podfile's plugins
506
+ #
507
+ # @return [Hash<String, Hash>] The plugins to be used
508
+ #
509
+ def plugins
510
+ if use_default_plugins?
511
+ DEFAULT_PLUGINS.merge(podfile.plugins)
512
+ else
513
+ podfile.plugins
514
+ end
515
+ end
516
+
517
+ # Prints a warning for any pods that are deprecated
518
+ #
519
+ # @return [void]
520
+ #
521
+ def warn_for_deprecations
522
+ deprecated_pods = root_specs.select do |spec|
523
+ spec.deprecated || spec.deprecated_in_favor_of
524
+ end
525
+ deprecated_pods.each do |spec|
526
+ if spec.deprecated_in_favor_of
527
+ UI.warn "#{spec.name} has been deprecated in " \
528
+ "favor of #{spec.deprecated_in_favor_of}"
529
+ else
530
+ UI.warn "#{spec.name} has been deprecated"
531
+ end
532
+ end
533
+ end
534
+
535
+ # Prints a warning for any pods that included script phases
536
+ #
537
+ # @return [void]
538
+ #
539
+ def warn_for_installed_script_phases
540
+ pods_to_install = sandbox_state.added | sandbox_state.changed
541
+ pod_targets.group_by(&:pod_name).each do |name, pod_targets|
542
+ if pods_to_install.include?(name)
543
+ script_phase_count = pod_targets.inject(0) { |sum, target| sum + target.script_phases.count }
544
+ unless script_phase_count.zero?
545
+ UI.warn "#{name} has added #{script_phase_count} #{'script phase'.pluralize(script_phase_count)}. " \
546
+ 'Please inspect before executing a build. See `https://guides.cocoapods.org/syntax/podspec.html#script_phases` for more information.'
547
+ end
548
+ end
549
+ end
550
+ end
551
+
552
+ # Writes the Podfile and the lock files.
553
+ #
554
+ # @return [void]
555
+ #
556
+ def write_lockfiles
557
+ external_source_pods = analysis_result.podfile_dependency_cache.podfile_dependencies.select(&:external_source).map(&:root_name).uniq
558
+ checkout_options = sandbox.checkout_sources.select { |root_name, _| external_source_pods.include? root_name }
559
+ @lockfile = Lockfile.generate(podfile, analysis_result.specifications, checkout_options, analysis_result.specs_by_source)
560
+
561
+ UI.message "- Writing Lockfile in #{UI.path config.lockfile_path}" do
562
+ @lockfile.write_to_disk(config.lockfile_path)
563
+ end
564
+
565
+ UI.message "- Writing Manifest in #{UI.path sandbox.manifest_path}" do
566
+ sandbox.manifest_path.open('w') do |f|
567
+ f.write config.lockfile_path.read
568
+ end
569
+ end
570
+ end
571
+
572
+ # Integrates the user projects adding the dependencies on the CocoaPods
573
+ # libraries, setting them up to use the xcconfigs and performing other
574
+ # actions. This step is also responsible of creating the workspace if
575
+ # needed.
576
+ #
577
+ # @return [void]
578
+ #
579
+ def integrate_user_project
580
+ UI.section "Integrating client #{'project'.pluralize(aggregate_targets.map(&:user_project_path).uniq.count)}" do
581
+ installation_root = config.installation_root
582
+ integrator = UserProjectIntegrator.new(podfile, sandbox, installation_root, aggregate_targets)
583
+ integrator.integrate!
584
+ end
585
+ end
586
+
587
+ #-------------------------------------------------------------------------#
588
+
589
+ private
590
+
591
+ # @!group Hooks
592
+
593
+ # Runs the pre install hooks of the installed specs and of the Podfile.
594
+ #
595
+ # @return [void]
596
+ #
597
+ def run_podfile_pre_install_hooks
598
+ UI.message '- Running pre install hooks' do
599
+ executed = run_podfile_pre_install_hook
600
+ UI.message '- Podfile' if executed
601
+ end
602
+ end
603
+
604
+ # Runs the pre install hook of the Podfile
605
+ #
606
+ # @raise Raises an informative if the hooks raises.
607
+ #
608
+ # @return [Boolean] Whether the hook was run.
609
+ #
610
+ def run_podfile_pre_install_hook
611
+ podfile.pre_install!(self)
612
+ rescue => e
613
+ raise Informative, 'An error occurred while processing the pre-install ' \
614
+ 'hook of the Podfile.' \
615
+ "\n\n#{e.message}\n\n#{e.backtrace * "\n"}"
616
+ end
617
+
618
+ # Runs the post install hooks of the installed specs and of the Podfile.
619
+ #
620
+ # @note Post install hooks run _before_ saving of project, so that they
621
+ # can alter it before it is written to the disk.
622
+ #
623
+ # @return [void]
624
+ #
625
+ def run_podfile_post_install_hooks
626
+ UI.message '- Running post install hooks' do
627
+ executed = run_podfile_post_install_hook
628
+ UI.message '- Podfile' if executed
629
+ end
630
+ end
631
+
632
+ # Runs the post install hook of the Podfile
633
+ #
634
+ # @raise Raises an informative if the hooks raises.
635
+ #
636
+ # @return [Boolean] Whether the hook was run.
637
+ #
638
+ def run_podfile_post_install_hook
639
+ podfile.post_install!(self)
640
+ rescue => e
641
+ raise Informative, 'An error occurred while processing the post-install ' \
642
+ 'hook of the Podfile.' \
643
+ "\n\n#{e.message}\n\n#{e.backtrace * "\n"}"
644
+ end
645
+
646
+ #-------------------------------------------------------------------------#
647
+
648
+ public
649
+
650
+ # @return [Array<PodTarget>] The targets of the development pods generated by
651
+ # the installation process. This can be used as a convenience method for external scripts.
652
+ #
653
+ def development_pod_targets
654
+ pod_targets.select do |pod_target|
655
+ sandbox.local?(pod_target.pod_name)
656
+ end
657
+ end
658
+
659
+ #-------------------------------------------------------------------------#
660
+
661
+ private
662
+
663
+ # @!group Private helpers
664
+
665
+ # @return [Array<Specification>] All the root specifications of the
666
+ # installation.
667
+ #
668
+ def root_specs
669
+ analysis_result.specifications.map(&:root).uniq
670
+ end
671
+
672
+ # @return [SpecsState] The state of the sandbox returned by the analyzer.
673
+ #
674
+ def sandbox_state
675
+ analysis_result.sandbox_state
676
+ end
677
+
678
+ #-------------------------------------------------------------------------#
679
+
680
+ public
681
+
682
+ # @!group Convenience Methods
683
+
684
+ def self.targets_from_sandbox(sandbox, podfile, lockfile)
685
+ raise Informative, 'You must run `pod install` to be able to generate target information' unless lockfile
686
+
687
+ new(sandbox, podfile, lockfile).instance_exec do
688
+ plugin_sources = run_source_provider_hooks
689
+ analyzer = create_analyzer(plugin_sources)
690
+ analyze(analyzer)
691
+ if analysis_result.podfile_needs_install?
692
+ raise Pod::Informative, 'The Podfile has changed, you must run `pod install`'
693
+ elsif analysis_result.sandbox_needs_install?
694
+ raise Pod::Informative, 'The `Pods` directory is out-of-date, you must run `pod install`'
695
+ end
696
+
697
+ aggregate_targets
698
+ end
699
+ end
700
+
701
+ #-------------------------------------------------------------------------#
702
+ end
703
+ end