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,972 @@
1
+ module Pod
2
+ class Installer
3
+ # Analyzes the Podfile, the Lockfile, and the sandbox manifest to generate
4
+ # the information relative to a CocoaPods installation.
5
+ #
6
+ class Analyzer
7
+ include Config::Mixin
8
+ include InstallationOptions::Mixin
9
+
10
+ delegate_installation_options { podfile }
11
+
12
+ autoload :AnalysisResult, 'cocoapods/installer/analyzer/analysis_result'
13
+ autoload :LockingDependencyAnalyzer, 'cocoapods/installer/analyzer/locking_dependency_analyzer'
14
+ autoload :PodfileDependencyCache, 'cocoapods/installer/analyzer/podfile_dependency_cache'
15
+ autoload :PodVariant, 'cocoapods/installer/analyzer/pod_variant'
16
+ autoload :PodVariantSet, 'cocoapods/installer/analyzer/pod_variant_set'
17
+ autoload :SandboxAnalyzer, 'cocoapods/installer/analyzer/sandbox_analyzer'
18
+ autoload :SpecsState, 'cocoapods/installer/analyzer/specs_state'
19
+ autoload :TargetInspectionResult, 'cocoapods/installer/analyzer/target_inspection_result'
20
+ autoload :TargetInspector, 'cocoapods/installer/analyzer/target_inspector'
21
+
22
+ # @return [Sandbox] The sandbox to use for this analysis.
23
+ #
24
+ attr_reader :sandbox
25
+
26
+ # @return [Podfile] The Podfile specification that contains the information of the Pods that should be installed.
27
+ #
28
+ attr_reader :podfile
29
+
30
+ # @return [Lockfile] The Lockfile, if available, that stores the information about the Pods previously installed.
31
+ #
32
+ attr_reader :lockfile
33
+
34
+ # @return [Array<Source>] Sources provided by plugins or `nil`.
35
+ #
36
+ attr_reader :plugin_sources
37
+
38
+ # @return [Bool] Whether the analysis has dependencies and thus sources must be configured.
39
+ #
40
+ # @note This is used by the `pod lib lint` command to prevent update of specs when not needed.
41
+ #
42
+ attr_reader :has_dependencies
43
+ alias_method :has_dependencies?, :has_dependencies
44
+
45
+ # @return [Hash, Boolean, nil] Pods that have been requested to be updated or true if all Pods should be updated.
46
+ # This can be false if no pods should be updated.
47
+ #
48
+ attr_reader :pods_to_update
49
+
50
+ # Initialize a new instance
51
+ #
52
+ # @param [Sandbox] sandbox @see #sandbox
53
+ # @param [Podfile] podfile @see #podfile
54
+ # @param [Lockfile] lockfile @see #lockfile
55
+ # @param [Array<Source>] plugin_sources @see #plugin_sources
56
+ # @param [Boolean] has_dependencies @see #has_dependencies
57
+ # @param [Hash, Boolean, nil] pods_to_update @see #pods_to_update
58
+ #
59
+ def initialize(sandbox, podfile, lockfile = nil, plugin_sources = nil, has_dependencies = true,
60
+ pods_to_update = false)
61
+ @sandbox = sandbox
62
+ @podfile = podfile
63
+ @lockfile = lockfile
64
+ @plugin_sources = plugin_sources
65
+ @has_dependencies = has_dependencies
66
+ @pods_to_update = pods_to_update
67
+ @podfile_dependency_cache = PodfileDependencyCache.from_podfile(podfile)
68
+ @result = nil
69
+ end
70
+
71
+ # Performs the analysis.
72
+ #
73
+ # The Podfile and the Lockfile provide the information necessary to
74
+ # compute which specification should be installed. The manifest of the
75
+ # sandbox returns which specifications are installed.
76
+ #
77
+ # @param [Bool] allow_fetches
78
+ # whether external sources may be fetched
79
+ #
80
+ # @return [AnalysisResult]
81
+ #
82
+ def analyze(allow_fetches = true)
83
+ return @result if @result
84
+ validate_podfile!
85
+ validate_lockfile_version!
86
+ if installation_options.integrate_targets?
87
+ target_inspections = inspect_targets_to_integrate
88
+ else
89
+ verify_platforms_specified!
90
+ target_inspections = {}
91
+ end
92
+ podfile_state = generate_podfile_state
93
+
94
+ store_existing_checkout_options
95
+ fetch_external_sources(podfile_state) if allow_fetches
96
+
97
+ locked_dependencies = generate_version_locking_dependencies(podfile_state)
98
+ resolver_specs_by_target = resolve_dependencies(locked_dependencies)
99
+ validate_platforms(resolver_specs_by_target)
100
+ specifications = generate_specifications(resolver_specs_by_target)
101
+ targets = generate_targets(resolver_specs_by_target, target_inspections)
102
+ pod_targets = calculate_pod_targets(targets)
103
+ sandbox_state = generate_sandbox_state(specifications)
104
+ specs_by_target = resolver_specs_by_target.each_with_object({}) do |rspecs_by_target, hash|
105
+ hash[rspecs_by_target[0]] = rspecs_by_target[1].map(&:spec)
106
+ end
107
+ specs_by_source = Hash[resolver_specs_by_target.values.flatten(1).group_by(&:source).map do |source, specs|
108
+ [source, specs.map(&:spec).uniq]
109
+ end]
110
+ sources.each { |s| specs_by_source[s] ||= [] }
111
+ @result = AnalysisResult.new(podfile_state, specs_by_target, specs_by_source, specifications, sandbox_state,
112
+ targets, pod_targets, @podfile_dependency_cache)
113
+ end
114
+
115
+ # Updates the git source repositories.
116
+ #
117
+ def update_repositories
118
+ sources.each do |source|
119
+ if source.git?
120
+ config.sources_manager.update(source.name, true)
121
+ else
122
+ UI.message "Skipping `#{source.name}` update because the repository is not a git source repository."
123
+ end
124
+ end
125
+ @specs_updated = true
126
+ end
127
+
128
+ # Returns the sources used to query for specifications.
129
+ #
130
+ # When no explicit Podfile sources or plugin sources are defined, this defaults to the master spec repository.
131
+ #
132
+ # @return [Array<Source>] the sources to be used in finding specifications, as specified by the podfile or all
133
+ # sources.
134
+ #
135
+ def sources
136
+ @sources ||= begin
137
+ sources = podfile.sources
138
+ plugin_sources = @plugin_sources || []
139
+
140
+ # Add any sources specified using the :source flag on individual dependencies.
141
+ dependency_sources = podfile_dependencies.map(&:podspec_repo).compact
142
+ all_dependencies_have_sources = dependency_sources.count == podfile_dependencies.count
143
+
144
+ if all_dependencies_have_sources
145
+ sources = dependency_sources
146
+ elsif has_dependencies? && sources.empty? && plugin_sources.empty?
147
+ sources = ['https://github.com/CocoaPods/Specs.git']
148
+ else
149
+ sources += dependency_sources
150
+ end
151
+
152
+ result = sources.uniq.map do |source_url|
153
+ config.sources_manager.find_or_create_source_with_url(source_url)
154
+ end
155
+ unless plugin_sources.empty?
156
+ result.insert(0, *plugin_sources)
157
+ end
158
+ result
159
+ end
160
+ end
161
+
162
+ #-----------------------------------------------------------------------#
163
+
164
+ private
165
+
166
+ # @!group Configuration
167
+
168
+ # @return [Bool] Whether the version of the dependencies which did not
169
+ # change in the Podfile should be locked.
170
+ #
171
+ def update_mode?
172
+ pods_to_update != nil
173
+ end
174
+
175
+ # @return [Symbol] Whether and how the dependencies in the Podfile
176
+ # should be updated.
177
+ #
178
+ def update_mode
179
+ if !pods_to_update
180
+ :none
181
+ elsif pods_to_update == true
182
+ :all
183
+ elsif !pods_to_update[:pods].nil?
184
+ :selected
185
+ end
186
+ end
187
+
188
+ def podfile_dependencies
189
+ @podfile_dependency_cache.podfile_dependencies
190
+ end
191
+
192
+ #-----------------------------------------------------------------------#
193
+
194
+ def validate_podfile!
195
+ validator = Installer::PodfileValidator.new(podfile, @podfile_dependency_cache)
196
+ validator.validate
197
+
198
+ unless validator.valid?
199
+ raise Informative, validator.message
200
+ end
201
+ validator.warnings.uniq.each { |w| UI.warn(w) }
202
+ end
203
+
204
+ # @!group Analysis steps
205
+
206
+ # @note The warning about the version of the Lockfile doesn't use the
207
+ # `UI.warn` method because it prints the output only at the end
208
+ # of the installation. At that time CocoaPods could have crashed.
209
+ #
210
+ def validate_lockfile_version!
211
+ if lockfile && lockfile.cocoapods_version > Version.new(VERSION)
212
+ STDERR.puts '[!] The version of CocoaPods used to generate ' \
213
+ "the lockfile (#{lockfile.cocoapods_version}) is "\
214
+ "higher than the version of the current executable (#{VERSION}). " \
215
+ 'Incompatibility issues may arise.'.yellow
216
+ end
217
+ end
218
+
219
+ # Compares the {Podfile} with the {Lockfile} in order to detect which
220
+ # dependencies should be locked.
221
+ #
222
+ # @return [SpecsState] the states of the Podfile specs.
223
+ #
224
+ # @note As the target definitions share the same sandbox they should have
225
+ # the same version of a Pod. For this reason this method returns
226
+ # the name of the Pod (root name of the dependencies) and doesn't
227
+ # group them by target definition.
228
+ #
229
+ # @return [SpecState]
230
+ #
231
+ def generate_podfile_state
232
+ if lockfile
233
+ pods_state = nil
234
+ UI.section 'Finding Podfile changes' do
235
+ pods_by_state = lockfile.detect_changes_with_podfile(podfile)
236
+ pods_state = SpecsState.new(pods_by_state)
237
+ pods_state.print if config.verbose?
238
+ end
239
+ pods_state
240
+ else
241
+ state = SpecsState.new
242
+ state.added.merge(podfile_dependencies.map(&:root_name))
243
+ state
244
+ end
245
+ end
246
+
247
+ # Copies the pod_targets of any of the app embedded aggregate targets into
248
+ # their potential host aggregate target, if that potential host aggregate target's
249
+ # user_target hosts any of the app embedded aggregate targets' user_targets
250
+ #
251
+ # @param [AggregateTarget] aggregate_target the aggregate target whose user_target
252
+ # might host one or more of the embedded aggregate targets' user_targets
253
+ #
254
+ # @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
255
+ # representing the embedded targets to be integrated
256
+ #
257
+ # @param [Boolean] libraries_only if true, only library-type embedded
258
+ # targets are considered, otherwise, all other types are have
259
+ # their pods copied to their host targets as well (extensions, etc.)
260
+ #
261
+ def copy_embedded_target_pod_targets_to_host(aggregate_target, embedded_aggregate_targets, libraries_only)
262
+ return if aggregate_target.requires_host_target?
263
+ pod_target_names = Set.new(aggregate_target.pod_targets.map(&:name))
264
+ aggregate_user_target_uuids = Set.new(aggregate_target.user_targets.map(&:uuid))
265
+ embedded_aggregate_targets.each do |embedded_aggregate_target|
266
+ # Skip non libraries in library-only mode
267
+ next if libraries_only && !embedded_aggregate_target.library?
268
+ next unless embedded_aggregate_target.user_targets.any? do |embedded_user_target|
269
+ # You have to ask the host target's project for the host targets of
270
+ # the embedded target, as opposed to asking user_project for the
271
+ # embedded targets of the host target. The latter doesn't work when
272
+ # the embedded target lives in a sub-project. The lines below get
273
+ # the host target uuids for the embedded target and checks to see if
274
+ # those match to any of the user_target uuids in the aggregate_target.
275
+ host_target_uuids = Set.new(aggregate_target.user_project.host_targets_for_embedded_target(embedded_user_target).map(&:uuid))
276
+ !aggregate_user_target_uuids.intersection(host_target_uuids).empty?
277
+ end
278
+ # This embedded target is hosted by the aggregate target's user_target; copy over the non-duplicate pod_targets
279
+ aggregate_target.pod_targets = aggregate_target.pod_targets + embedded_aggregate_target.pod_targets.select do |pod_target|
280
+ !pod_target_names.include? pod_target.name
281
+ end
282
+ end
283
+ end
284
+
285
+ # Raises an error if there are embedded targets in the Podfile, but
286
+ # their host targets have not been declared in the Podfile. As it
287
+ # finds host targets, it collection information on host target types.
288
+ #
289
+ # @param [Array<AggregateTarget>] aggregate_targets the generated
290
+ # aggregate targets
291
+ #
292
+ # @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
293
+ # representing the embedded targets to be integrated
294
+ #
295
+ def analyze_host_targets_in_podfile(aggregate_targets, embedded_aggregate_targets)
296
+ target_definitions_by_uuid = {}
297
+ # Collect aggregate target definitions by uuid to later lookup host target
298
+ # definitions and verify their compatiblity with their embedded targets
299
+ aggregate_targets.each do |target|
300
+ target.user_targets.map(&:uuid).each do |uuid|
301
+ target_definitions_by_uuid[uuid] = target.target_definition
302
+ end
303
+ end
304
+ aggregate_target_user_projects = aggregate_targets.map(&:user_project)
305
+ embedded_targets_missing_hosts = []
306
+ host_uuid_to_embedded_target_definitions = {}
307
+ # Search all of the known user projects for each embedded target's hosts
308
+ embedded_aggregate_targets.each do |target|
309
+ host_uuids = []
310
+ aggregate_target_user_projects.product(target.user_targets).each do |user_project, user_target|
311
+ host_uuids += user_project.host_targets_for_embedded_target(user_target).map(&:uuid)
312
+ end
313
+ # For each host, keep track of its embedded target definitions
314
+ # to later verify each embedded target's compatiblity with its host,
315
+ # ignoring the hosts that aren't known to CocoaPods (no target
316
+ # definitions in the Podfile)
317
+ host_uuids.each do |uuid|
318
+ (host_uuid_to_embedded_target_definitions[uuid] ||= []) << target.target_definition if target_definitions_by_uuid.key? uuid
319
+ end
320
+ # If none of the hosts are known to CocoaPods (no target definitions
321
+ # in the Podfile), add it to the list of targets missing hosts
322
+ embedded_targets_missing_hosts << target unless host_uuids.any? do |uuid|
323
+ target_definitions_by_uuid.key? uuid
324
+ end
325
+ end
326
+
327
+ unless embedded_targets_missing_hosts.empty?
328
+ embedded_targets_missing_hosts_product_types = Set.new embedded_targets_missing_hosts.flat_map(&:user_targets).map(&:symbol_type)
329
+ target_names = embedded_targets_missing_hosts.map do |target|
330
+ target.name.sub('Pods-', '') # Make the target names more recognizable to the user
331
+ end.join ', '
332
+ # If the targets missing hosts are only frameworks, then this is likely
333
+ # a project for doing framework development. In that case, just warn that
334
+ # the frameworks that these targets depend on won't be integrated anywhere
335
+ if embedded_targets_missing_hosts_product_types.subset?(Set.new([:framework, :static_library]))
336
+ UI.warn "The Podfile contains framework or static library targets (#{target_names}), for which the Podfile does not contain host targets (targets which embed the framework)." \
337
+ "\n" \
338
+ 'If this project is for doing framework development, you can ignore this message. Otherwise, add a target to the Podfile that embeds these frameworks to make this message go away (e.g. a test target).'
339
+ else
340
+ raise Informative, "Unable to find host target(s) for #{target_names}. Please add the host targets for the embedded targets to the Podfile." \
341
+ "\n" \
342
+ 'Certain kinds of targets require a host target. A host target is a "parent" target which embeds a "child" target. These are example types of targets that need a host target:' \
343
+ "\n- Framework" \
344
+ "\n- App Extension" \
345
+ "\n- Watch OS 1 Extension" \
346
+ "\n- Messages Extension (except when used with a Messages Application)"
347
+ end
348
+ end
349
+
350
+ target_mismatches = []
351
+ host_uuid_to_embedded_target_definitions.each do |uuid, target_definitions|
352
+ host_target_definition = target_definitions_by_uuid[uuid]
353
+ target_definitions.each do |target_definition|
354
+ unless host_target_definition.uses_frameworks? == target_definition.uses_frameworks?
355
+ target_mismatches << "- #{host_target_definition.name} (#{host_target_definition.uses_frameworks?}) and #{target_definition.name} (#{target_definition.uses_frameworks?}) do not both set use_frameworks!."
356
+ end
357
+ end
358
+ end
359
+
360
+ unless target_mismatches.empty?
361
+ heading = 'Unable to integrate the following embedded targets with their respective host targets (a host target is a "parent" target which embeds a "child" target like a framework or extension):'
362
+ raise Informative, heading + "\n\n" + target_mismatches.sort.uniq.join("\n")
363
+ end
364
+ end
365
+
366
+ # Creates the models that represent the targets generated by CocoaPods.
367
+ #
368
+ # @param [Hash{Podfile::TargetDefinition => Array<ResolvedSpecification>}] resolver_specs_by_target
369
+ # mapping of targets to resolved specs (containing information about test usage)
370
+ # aggregate targets
371
+ #
372
+ # @param [Array<TargetInspection>] target_inspections
373
+ # the user target inspections used to construct the aggregate and pod targets.
374
+ #
375
+ # @return [Array<AggregateTarget>] the list of aggregate targets generated.
376
+ #
377
+ def generate_targets(resolver_specs_by_target, target_inspections)
378
+ resolver_specs_by_target = resolver_specs_by_target.reject { |td, _| td.abstract? }
379
+ pod_targets = generate_pod_targets(resolver_specs_by_target, target_inspections)
380
+ aggregate_targets = resolver_specs_by_target.keys.map do |target_definition|
381
+ generate_target(target_definition, target_inspections, pod_targets, resolver_specs_by_target)
382
+ end
383
+ if installation_options.integrate_targets?
384
+ # Copy embedded target pods that cannot have their pods embedded as frameworks to
385
+ # their host targets, and ensure we properly link library pods to their host targets
386
+ embedded_targets = aggregate_targets.select(&:requires_host_target?)
387
+ analyze_host_targets_in_podfile(aggregate_targets, embedded_targets)
388
+
389
+ use_frameworks_embedded_targets, non_use_frameworks_embedded_targets = embedded_targets.partition(&:requires_frameworks?)
390
+ aggregate_targets.each do |target|
391
+ # For targets that require frameworks, we always have to copy their pods to their
392
+ # host targets because those frameworks will all be loaded from the host target's bundle
393
+ copy_embedded_target_pod_targets_to_host(target, use_frameworks_embedded_targets, false)
394
+
395
+ # For targets that don't require frameworks, we only have to consider library-type
396
+ # targets because their host targets will still need to link their pods
397
+ copy_embedded_target_pod_targets_to_host(target, non_use_frameworks_embedded_targets, true)
398
+ end
399
+ end
400
+ aggregate_targets.each do |target|
401
+ target.search_paths_aggregate_targets.concat(aggregate_targets.select do |aggregate_target|
402
+ target.target_definition.targets_to_inherit_search_paths.include?(aggregate_target.target_definition)
403
+ end).freeze
404
+ end
405
+ end
406
+
407
+ # Setup the aggregate target for a single user target
408
+ #
409
+ # @param [TargetDefinition] target_definition
410
+ # the target definition for the user target.
411
+ #
412
+ # @param [Array<TargetInspection>] target_inspections
413
+ # the user target inspections used to construct the aggregate and pod targets.
414
+ #
415
+ # @param [Array<PodTarget>] pod_targets
416
+ # the pod targets, which were generated.
417
+ #
418
+ # @param [Hash{Podfile::TargetDefinition => Array<ResolvedSpecification>}] resolver_specs_by_target
419
+ # the resolved specifications grouped by target.
420
+ #
421
+ # @return [AggregateTarget]
422
+ #
423
+ def generate_target(target_definition, target_inspections, pod_targets, resolver_specs_by_target)
424
+ if installation_options.integrate_targets?
425
+ target_inspection = target_inspections[target_definition]
426
+ raise "missing inspection: #{target_definition.name}" unless target_inspection
427
+ user_project = target_inspection.project
428
+ client_root = user_project.path.dirname.realpath
429
+ user_target_uuids = target_inspection.project_target_uuids
430
+ user_build_configurations = target_inspection.build_configurations
431
+ archs = target_inspection.archs
432
+ else
433
+ user_project = nil
434
+ client_root = config.installation_root.realpath
435
+ user_target_uuids = []
436
+ user_build_configurations = target_definition.build_configurations || Target::DEFAULT_BUILD_CONFIGURATIONS
437
+ archs = []
438
+ if target_definition.platform && target_definition.platform.name == :osx
439
+ archs = ['$(ARCHS_STANDARD_64_BIT)']
440
+ end
441
+ end
442
+ platform = target_definition.platform
443
+ build_configurations = user_build_configurations.keys.concat(target_definition.all_whitelisted_configurations).uniq
444
+ pod_targets = filter_pod_targets_for_target_definition(target_definition, pod_targets, resolver_specs_by_target, build_configurations)
445
+ AggregateTarget.new(sandbox, target_definition.uses_frameworks?, user_build_configurations, archs, platform,
446
+ target_definition, client_root, user_project, user_target_uuids, pod_targets)
447
+ end
448
+
449
+ # @return [Array<PodTarget>] The model representations of pod targets.
450
+ #
451
+ def calculate_pod_targets(aggregate_targets)
452
+ aggregate_target_pod_targets = aggregate_targets.flat_map(&:pod_targets).uniq
453
+ test_dependent_targets = aggregate_target_pod_targets.flat_map do |pod_target|
454
+ pod_target.test_dependent_targets_by_spec_name.values.flatten
455
+ end
456
+ (aggregate_target_pod_targets + test_dependent_targets).uniq
457
+ end
458
+
459
+ # Returns a filtered list of pod targets that should or should not be part of the target definition. Pod targets
460
+ # used by tests only are filtered.
461
+ #
462
+ # @param [TargetDefinition] target_definition
463
+ # the target definition to use as the base for filtering
464
+ #
465
+ # @param [Array<PodTarget>] pod_targets
466
+ # the array of pod targets to check against
467
+ #
468
+ # @param [Hash{Podfile::TargetDefinition => Array<ResolvedSpecification>}] resolver_specs_by_target
469
+ # the resolved specifications grouped by target.
470
+ #
471
+ # @param [Array<String>] build_configurations
472
+ # The list of all build configurations the targets will be built for.
473
+ #
474
+ # @return [Hash<String => Array<PodTarget>>]
475
+ # the filtered list of pod targets, grouped by build configuration.
476
+ #
477
+ def filter_pod_targets_for_target_definition(target_definition, pod_targets, resolver_specs_by_target, build_configurations)
478
+ pod_targets_by_build_config = Hash.new([].freeze)
479
+ build_configurations.each { |config| pod_targets_by_build_config[config] = [] }
480
+
481
+ pod_targets.each do |pod_target|
482
+ next unless pod_target.target_definitions.include?(target_definition)
483
+ next unless resolver_specs_by_target[target_definition].any? do |resolver_spec|
484
+ !resolver_spec.used_by_tests_only? && pod_target.specs.include?(resolver_spec.spec)
485
+ end
486
+
487
+ pod_name = pod_target.pod_name
488
+
489
+ dependencies = @podfile_dependency_cache.target_definition_dependencies(target_definition).select do |dependency|
490
+ Specification.root_name(dependency.name) == pod_name
491
+ end
492
+
493
+ build_configurations.each do |configuration_name|
494
+ whitelists = dependencies.map do |dependency|
495
+ target_definition.pod_whitelisted_for_configuration?(dependency.name, configuration_name)
496
+ end.uniq
497
+
498
+ case whitelists
499
+ when [], [true] then nil
500
+ when [false] then next
501
+ else
502
+ raise Informative, "The subspecs of `#{pod_name}` are linked to " \
503
+ "different build configurations for the `#{target_definition}` " \
504
+ 'target. CocoaPods does not currently support subspecs across ' \
505
+ 'different build configurations.'
506
+ end
507
+
508
+ pod_targets_by_build_config[configuration_name] << pod_target
509
+ end
510
+ end
511
+
512
+ pod_targets_by_build_config
513
+ end
514
+
515
+ # Setup the pod targets for an aggregate target. Deduplicates resulting
516
+ # targets by grouping by platform and subspec by their root
517
+ # to create a {PodTarget} for each spec.
518
+ #
519
+ # @param [Hash{Podfile::TargetDefinition => Array<ResolvedSpecification>}] resolver_specs_by_target
520
+ # the resolved specifications grouped by target.
521
+ #
522
+ # @param [Array<TargetInspection>] target_inspections
523
+ # the user target inspections used to construct the aggregate and pod targets.
524
+ #
525
+ # @return [Array<PodTarget>]
526
+ #
527
+ def generate_pod_targets(resolver_specs_by_target, target_inspections)
528
+ if installation_options.deduplicate_targets?
529
+ distinct_targets = resolver_specs_by_target.each_with_object({}) do |dependency, hash|
530
+ target_definition, dependent_specs = *dependency
531
+ dependent_specs.group_by(&:root).each do |root_spec, resolver_specs|
532
+ all_specs = resolver_specs.map(&:spec)
533
+ test_specs, specs = all_specs.partition(&:test_specification?)
534
+ pod_variant = PodVariant.new(specs, test_specs, target_definition.platform, target_definition.uses_frameworks?)
535
+ hash[root_spec] ||= {}
536
+ (hash[root_spec][pod_variant] ||= []) << target_definition
537
+ hash[root_spec].keys.find { |k| k == pod_variant }.test_specs.concat(test_specs).uniq!
538
+ end
539
+ end
540
+
541
+ pod_targets = distinct_targets.flat_map do |_root, target_definitions_by_variant|
542
+ suffixes = PodVariantSet.new(target_definitions_by_variant.keys).scope_suffixes
543
+ target_definitions_by_variant.flat_map do |variant, target_definitions|
544
+ generate_pod_target(target_definitions, target_inspections, variant.specs + variant.test_specs, :scope_suffix => suffixes[variant])
545
+ end
546
+ end
547
+
548
+ all_resolver_specs = resolver_specs_by_target.values.flatten.map(&:spec).uniq
549
+ pod_targets_by_name = pod_targets.group_by(&:pod_name).each_with_object({}) do |(name, values), hash|
550
+ # Sort the target by the number of activated subspecs, so that
551
+ # we prefer a minimal target as transitive dependency.
552
+ hash[name] = values.sort_by { |pt| pt.specs.count }
553
+ end
554
+ pod_targets.each do |target|
555
+ all_specs = all_resolver_specs.group_by(&:name)
556
+ dependencies = dependencies_for_specs(target.non_test_specs.to_set, target.platform, all_specs.dup).group_by(&:root)
557
+ target.dependent_targets = filter_dependencies(dependencies, pod_targets_by_name, target)
558
+ target.test_dependent_targets_by_spec_name = target.test_specs.each_with_object({}) do |test_spec, hash|
559
+ test_dependencies = dependencies_for_specs([test_spec], target.platform, all_specs).group_by(&:root)
560
+ test_dependencies.delete_if { |k| dependencies.key? k }
561
+ hash[test_spec.name] = filter_dependencies(test_dependencies, pod_targets_by_name, target)
562
+ end
563
+ end
564
+ else
565
+ dedupe_cache = {}
566
+ resolver_specs_by_target.flat_map do |target_definition, specs|
567
+ grouped_specs = specs.group_by(&:root).values.uniq
568
+ pod_targets = grouped_specs.flat_map do |pod_specs|
569
+ generate_pod_target([target_definition], target_inspections, pod_specs.map(&:spec)).scoped(dedupe_cache)
570
+ end
571
+
572
+ pod_targets.each do |target|
573
+ all_specs = specs.map(&:spec).group_by(&:name)
574
+ dependencies = dependencies_for_specs(target.non_test_specs.to_set, target.platform, all_specs.dup).group_by(&:root)
575
+ target.dependent_targets = pod_targets.reject { |t| dependencies[t.root_spec].nil? }
576
+ target.test_dependent_targets_by_spec_name = target.test_specs.each_with_object({}) do |test_spec, hash|
577
+ test_dependencies = dependencies_for_specs(target.test_specs.to_set, target.platform, all_specs.dup).group_by(&:root)
578
+ test_dependencies.delete_if { |k| dependencies.key? k }
579
+ hash[test_spec.name] = pod_targets.reject { |t| test_dependencies[t.root_spec].nil? }
580
+ end
581
+ end
582
+ end
583
+ end
584
+ end
585
+
586
+ def filter_dependencies(dependencies, pod_targets_by_name, target)
587
+ dependencies.map do |root_spec, deps|
588
+ pod_targets_by_name[root_spec.name].find do |t|
589
+ next false if t.platform.symbolic_name != target.platform.symbolic_name ||
590
+ t.requires_frameworks? != target.requires_frameworks?
591
+ spec_names = t.specs.map(&:name)
592
+ deps.all? { |dep| spec_names.include?(dep.name) }
593
+ end
594
+ end
595
+ end
596
+
597
+ # Returns the specs upon which the given specs _directly_ depend.
598
+ #
599
+ # @note: This is implemented in the analyzer, because we don't have to
600
+ # care about the requirements after dependency resolution.
601
+ #
602
+ # @param [Array<Specification>] specs
603
+ # The specs, whose dependencies should be returned.
604
+ #
605
+ # @param [Platform] platform
606
+ # The platform for which the dependencies should be returned.
607
+ #
608
+ # @param [Hash<String, Specification>] all_specs
609
+ # All specifications which are installed alongside.
610
+ #
611
+ # @return [Array<Specification>]
612
+ #
613
+ def dependencies_for_specs(specs, platform, all_specs)
614
+ return [] if specs.empty? || all_specs.empty?
615
+
616
+ dependent_specs = Set.new
617
+
618
+ specs.each do |s|
619
+ s.dependencies(platform).each do |dep|
620
+ all_specs[dep.name].each do |spec|
621
+ dependent_specs << spec
622
+ end
623
+ end
624
+ end
625
+
626
+ dependent_specs - specs
627
+ end
628
+
629
+ # Create a target for each spec group
630
+ #
631
+ # @param [TargetDefinitions] target_definitions
632
+ # the aggregate target
633
+ #
634
+ # @param [Array<TargetInspection>] target_inspections
635
+ # the user target inspections used to construct the aggregate and pod targets.
636
+ #
637
+ # @param [Array<Specification>] specs
638
+ # the specifications of an equal root.
639
+ #
640
+ # @param [String] scope_suffix
641
+ # @see PodTarget#scope_suffix
642
+ #
643
+ # @return [PodTarget]
644
+ #
645
+ def generate_pod_target(target_definitions, target_inspections, specs, scope_suffix: nil)
646
+ if installation_options.integrate_targets?
647
+ target_inspections = target_inspections.select { |t, _| target_definitions.include?(t) }.values
648
+ user_build_configurations = target_inspections.map(&:build_configurations).reduce({}, &:merge)
649
+ archs = target_inspections.flat_map(&:archs).compact.uniq.sort
650
+ else
651
+ user_build_configurations = {}
652
+ archs = []
653
+ if target_definitions.first.platform.name == :osx
654
+ archs = ['$(ARCHS_STANDARD_64_BIT)']
655
+ end
656
+ end
657
+ host_requires_frameworks = target_definitions.any?(&:uses_frameworks?)
658
+ platform = determine_platform(specs, target_definitions, host_requires_frameworks)
659
+ file_accessors = create_file_accessors(specs, platform)
660
+ PodTarget.new(sandbox, host_requires_frameworks, user_build_configurations, archs, platform, specs,
661
+ target_definitions, file_accessors, scope_suffix)
662
+ end
663
+
664
+ # Creates the file accessors for a given pod.
665
+ #
666
+ # @param [Array<Specification>] specs
667
+ # the specs to map each file accessor to.
668
+ #
669
+ # @param [Platform] platform
670
+ # the platform to use when generating each file accessor.
671
+ #
672
+ # @return [Array<FileAccessor>]
673
+ #
674
+ def create_file_accessors(specs, platform)
675
+ name = specs.first.name
676
+ pod_root = sandbox.pod_dir(name)
677
+ path_list = Sandbox::PathList.new(pod_root)
678
+ specs.map do |spec|
679
+ Sandbox::FileAccessor.new(path_list, spec.consumer(platform))
680
+ end
681
+ end
682
+
683
+ # Calculates and returns the platform to use for the given list of specs and target definitions.
684
+ #
685
+ # @param [Array<Specification>] specs
686
+ # the specs to inspect and calculate the platform for.
687
+ #
688
+ # @param [Array<TargetDefinition>] target_definitions
689
+ # the target definitions these specs are part of.
690
+ #
691
+ # @param [Boolean] host_requires_frameworks
692
+ # whether the platform is calculated for a target that needs to be packaged as a framework.
693
+ #
694
+ # @return [Platform]
695
+ #
696
+ def determine_platform(specs, target_definitions, host_requires_frameworks)
697
+ platform_name = target_definitions.first.platform.name
698
+ default = Podfile::TargetDefinition::PLATFORM_DEFAULTS[platform_name]
699
+ deployment_target = specs.map do |spec|
700
+ Version.new(spec.deployment_target(platform_name) || default)
701
+ end.max
702
+ if platform_name == :ios && host_requires_frameworks
703
+ minimum = Version.new('8.0')
704
+ deployment_target = [deployment_target, minimum].max
705
+ end
706
+ Platform.new(platform_name, deployment_target)
707
+ end
708
+
709
+ # Generates dependencies that require the specific version of the Pods
710
+ # that haven't changed in the {Lockfile}.
711
+ #
712
+ # These dependencies are passed to the {Resolver}, unless the installer
713
+ # is in update mode, to prevent it from upgrading the Pods that weren't
714
+ # changed in the {Podfile}.
715
+ #
716
+ # @param [SpecState] podfile_state
717
+ # the state of the podfile for which dependencies have or have not changed, added, deleted or updated.
718
+ #
719
+ # @return [Molinillo::DependencyGraph<Dependency>] the dependencies
720
+ # generated by the lockfile that prevent the resolver to update
721
+ # a Pod.
722
+ #
723
+ def generate_version_locking_dependencies(podfile_state)
724
+ if update_mode == :all || !lockfile
725
+ LockingDependencyAnalyzer.unlocked_dependency_graph
726
+ else
727
+ deleted_and_changed = podfile_state.changed + podfile_state.deleted
728
+ deleted_and_changed += pods_to_update[:pods] if update_mode == :selected
729
+ local_pod_names = podfile_dependencies.select(&:local?).map(&:root_name)
730
+ pods_to_unlock = local_pod_names.to_set.delete_if do |pod_name|
731
+ next unless sandbox_specification = sandbox.specification(pod_name)
732
+ sandbox_specification.checksum == lockfile.checksum(pod_name)
733
+ end
734
+ LockingDependencyAnalyzer.generate_version_locking_dependencies(lockfile, deleted_and_changed, pods_to_unlock)
735
+ end
736
+ end
737
+
738
+ # Fetches the podspecs of external sources if modifications to the
739
+ # sandbox are allowed.
740
+ #
741
+ # @note In update mode all the external sources are refreshed while in
742
+ # normal mode they are refreshed only if added or changed in the
743
+ # Podfile. Moreover, in normal specifications for unchanged Pods
744
+ # which are missing or are generated from an local source are
745
+ # fetched as well.
746
+ #
747
+ # @note It is possible to perform this step before the resolution
748
+ # process because external sources identify a single specific
749
+ # version (checkout). If the other dependencies are not
750
+ # compatible with the version reported by the podspec of the
751
+ # external source the resolver will raise.
752
+ #
753
+ # @param [SpecState] podfile_state
754
+ # the state of the podfile for which dependencies have or have not changed, added, deleted or updated.
755
+ #
756
+ # @return [void]
757
+ #
758
+ def fetch_external_sources(podfile_state)
759
+ verify_no_pods_with_different_sources!
760
+ unless dependencies_to_fetch(podfile_state).empty?
761
+ UI.section 'Fetching external sources' do
762
+ dependencies_to_fetch(podfile_state).sort.each do |dependency|
763
+ fetch_external_source(dependency, !pods_to_fetch(podfile_state).include?(dependency.root_name))
764
+ end
765
+ end
766
+ end
767
+ end
768
+
769
+ def verify_no_pods_with_different_sources!
770
+ deps_with_different_sources = podfile_dependencies.group_by(&:root_name).
771
+ select { |_root_name, dependencies| dependencies.map(&:external_source).uniq.count > 1 }
772
+ deps_with_different_sources.each do |root_name, dependencies|
773
+ raise Informative, 'There are multiple dependencies with different ' \
774
+ "sources for `#{root_name}` in #{UI.path podfile.defined_in_file}:" \
775
+ "\n\n- #{dependencies.map(&:to_s).join("\n- ")}"
776
+ end
777
+ end
778
+
779
+ def fetch_external_source(dependency, use_lockfile_options)
780
+ checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name) if lockfile
781
+ source = if checkout_options && use_lockfile_options
782
+ ExternalSources.from_params(checkout_options, dependency, podfile.defined_in_file, installation_options.clean?)
783
+ else
784
+ ExternalSources.from_dependency(dependency, podfile.defined_in_file, installation_options.clean?)
785
+ end
786
+ source.fetch(sandbox)
787
+ end
788
+
789
+ def dependencies_to_fetch(podfile_state)
790
+ @deps_to_fetch ||= begin
791
+ deps_to_fetch = []
792
+ deps_with_external_source = podfile_dependencies.select(&:external_source)
793
+
794
+ if update_mode == :all
795
+ deps_to_fetch = deps_with_external_source
796
+ else
797
+ deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch(podfile_state).include?(dep.root_name) }
798
+ deps_to_fetch_if_needed = deps_with_external_source.select { |dep| podfile_state.unchanged.include?(dep.root_name) }
799
+ deps_to_fetch += deps_to_fetch_if_needed.select do |dep|
800
+ sandbox.specification_path(dep.root_name).nil? ||
801
+ !dep.external_source[:path].nil? ||
802
+ !sandbox.pod_dir(dep.root_name).directory? ||
803
+ checkout_requires_update?(dep)
804
+ end
805
+ end
806
+ deps_to_fetch.uniq(&:root_name)
807
+ end
808
+ end
809
+
810
+ def checkout_requires_update?(dependency)
811
+ return true unless lockfile && sandbox.manifest
812
+ locked_checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name)
813
+ sandbox_checkout_options = sandbox.manifest.checkout_options_for_pod_named(dependency.root_name)
814
+ locked_checkout_options != sandbox_checkout_options
815
+ end
816
+
817
+ def pods_to_fetch(podfile_state)
818
+ @pods_to_fetch ||= begin
819
+ pods_to_fetch = podfile_state.added + podfile_state.changed
820
+ if update_mode == :selected
821
+ pods_to_fetch += pods_to_update[:pods]
822
+ elsif update_mode == :all
823
+ pods_to_fetch += podfile_state.unchanged + podfile_state.deleted
824
+ end
825
+ pods_to_fetch += podfile_dependencies.
826
+ select { |dep| Hash(dep.external_source).key?(:podspec) && sandbox.specification_path(dep.root_name).nil? }.
827
+ map(&:root_name)
828
+ pods_to_fetch
829
+ end
830
+ end
831
+
832
+ def store_existing_checkout_options
833
+ podfile_dependencies.select(&:external_source).each do |dep|
834
+ if checkout_options = lockfile && lockfile.checkout_options_for_pod_named(dep.root_name)
835
+ sandbox.store_checkout_source(dep.root_name, checkout_options)
836
+ end
837
+ end
838
+ end
839
+
840
+ # Converts the Podfile in a list of specifications grouped by target.
841
+ #
842
+ # @note As some dependencies might have external sources the resolver
843
+ # is aware of the {Sandbox} and interacts with it to download the
844
+ # podspecs of the external sources. This is necessary because the
845
+ # resolver needs their specifications to analyze their
846
+ # dependencies.
847
+ #
848
+ # @note The specifications of the external sources which are added,
849
+ # modified or removed need to deleted from the sandbox before the
850
+ # resolution process. Otherwise the resolver might use an
851
+ # incorrect specification instead of pre-downloading it.
852
+ #
853
+ # @note In update mode the resolver is set to always update the specs
854
+ # from external sources.
855
+ #
856
+ # @return [Hash{TargetDefinition => Array<Spec>}] the specifications
857
+ # grouped by target.
858
+ #
859
+ def resolve_dependencies(locked_dependencies)
860
+ duplicate_dependencies = podfile_dependencies.group_by(&:name).
861
+ select { |_name, dependencies| dependencies.count > 1 }
862
+ duplicate_dependencies.each do |name, dependencies|
863
+ UI.warn "There are duplicate dependencies on `#{name}` in #{UI.path podfile.defined_in_file}:\n\n" \
864
+ "- #{dependencies.map(&:to_s).join("\n- ")}"
865
+ end
866
+
867
+ resolver_specs_by_target = nil
868
+ UI.section "Resolving dependencies of #{UI.path(podfile.defined_in_file) || 'Podfile'}" do
869
+ resolver = Pod::Resolver.new(sandbox, podfile, locked_dependencies, sources, @specs_updated)
870
+ resolver_specs_by_target = resolver.resolve
871
+ resolver_specs_by_target.values.flatten(1).map(&:spec).each(&:validate_cocoapods_version)
872
+ end
873
+ resolver_specs_by_target
874
+ end
875
+
876
+ # Warns for any specification that is incompatible with its target.
877
+ #
878
+ # @param [Hash{TargetDefinition => Array<Spec>}] resolver_specs_by_target
879
+ # the resolved specifications grouped by target.
880
+ #
881
+ # @return [Hash{TargetDefinition => Array<Spec>}] the specifications
882
+ # grouped by target.
883
+ #
884
+ def validate_platforms(resolver_specs_by_target)
885
+ resolver_specs_by_target.each do |target, specs|
886
+ specs.map(&:spec).each do |spec|
887
+ next unless target_platform = target.platform
888
+ unless spec.available_platforms.any? { |p| target_platform.supports?(p) }
889
+ UI.warn "The platform of the target `#{target.name}` " \
890
+ "(#{target.platform}) may not be compatible with `#{spec}` which has " \
891
+ "a minimum requirement of #{spec.available_platforms.join(' - ')}."
892
+ end
893
+ end
894
+ end
895
+ end
896
+
897
+ # Returns the list of all the resolved specifications.
898
+ #
899
+ # @param [Hash{TargetDefinition => Array<Spec>}] resolver_specs_by_target
900
+ # the resolved specifications grouped by target.
901
+ #
902
+ # @return [Array<Specification>] the list of the specifications.
903
+ #
904
+ def generate_specifications(resolver_specs_by_target)
905
+ resolver_specs_by_target.values.flatten.map(&:spec).uniq
906
+ end
907
+
908
+ # Computes the state of the sandbox respect to the resolved
909
+ # specifications.
910
+ #
911
+ # @return [SpecsState] the representation of the state of the manifest
912
+ # specifications.
913
+ #
914
+ def generate_sandbox_state(specifications)
915
+ sandbox_state = nil
916
+ UI.section 'Comparing resolved specification to the sandbox manifest' do
917
+ sandbox_analyzer = SandboxAnalyzer.new(sandbox, specifications, update_mode?)
918
+ sandbox_state = sandbox_analyzer.analyze
919
+ sandbox_state.print
920
+ end
921
+ sandbox_state
922
+ end
923
+
924
+ #-----------------------------------------------------------------------#
925
+
926
+ # @!group Analysis sub-steps
927
+
928
+ # Checks whether the platform is specified if not integrating
929
+ #
930
+ # @return [void]
931
+ #
932
+ def verify_platforms_specified!
933
+ unless installation_options.integrate_targets?
934
+ @podfile_dependency_cache.target_definition_list.each do |target_definition|
935
+ if !target_definition.empty? && target_definition.platform.nil?
936
+ raise Informative, 'It is necessary to specify the platform in the Podfile if not integrating.'
937
+ end
938
+ end
939
+ end
940
+ end
941
+
942
+ # Precompute information for each target_definition in the Podfile
943
+ #
944
+ # @note The platforms are computed and added to each target_definition
945
+ # because it might be necessary to infer the platform from the
946
+ # user targets.
947
+ #
948
+ # @return [Hash{TargetDefinition => TargetInspectionResult}]
949
+ #
950
+ def inspect_targets_to_integrate
951
+ inspection_result = {}
952
+ UI.section 'Inspecting targets to integrate' do
953
+ inspectors = @podfile_dependency_cache.target_definition_list.map do |target_definition|
954
+ next if target_definition.abstract?
955
+ TargetInspector.new(target_definition, config.installation_root)
956
+ end.compact
957
+ inspectors.group_by(&:compute_project_path).each do |project_path, target_inspectors|
958
+ project = Xcodeproj::Project.open(project_path)
959
+ target_inspectors.each do |inspector|
960
+ target_definition = inspector.target_definition
961
+ results = inspector.compute_results(project)
962
+ inspection_result[target_definition] = results
963
+ UI.message('Using `ARCHS` setting to build architectures of ' \
964
+ "target `#{target_definition.label}`: (`#{results.archs.join('`, `')}`)")
965
+ end
966
+ end
967
+ end
968
+ inspection_result
969
+ end
970
+ end
971
+ end
972
+ end