xcocoapods 1.5.3

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