cocoapods-tt 0.0.1

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/lib/cocoapods-tt/command/native/install.rb +56 -0
  3. data/lib/cocoapods-tt/command/native/update.rb +157 -0
  4. data/lib/cocoapods-tt/command/tt/make.rb +92 -0
  5. data/lib/cocoapods-tt/command/tt.rb +115 -0
  6. data/lib/cocoapods-tt/command.rb +1 -0
  7. data/lib/cocoapods-tt/gem_version.rb +3 -0
  8. data/lib/cocoapods-tt/native/command.rb +185 -0
  9. data/lib/cocoapods-tt/native/config.rb +366 -0
  10. data/lib/cocoapods-tt/native/core_overrides.rb +1 -0
  11. data/lib/cocoapods-tt/native/downloader/cache.rb +322 -0
  12. data/lib/cocoapods-tt/native/downloader/request.rb +86 -0
  13. data/lib/cocoapods-tt/native/downloader/response.rb +16 -0
  14. data/lib/cocoapods-tt/native/downloader.rb +192 -0
  15. data/lib/cocoapods-tt/native/executable.rb +247 -0
  16. data/lib/cocoapods-tt/native/external_sources/abstract_external_source.rb +205 -0
  17. data/lib/cocoapods-tt/native/external_sources/downloader_source.rb +30 -0
  18. data/lib/cocoapods-tt/native/external_sources/path_source.rb +55 -0
  19. data/lib/cocoapods-tt/native/external_sources/podspec_source.rb +54 -0
  20. data/lib/cocoapods-tt/native/external_sources.rb +57 -0
  21. data/lib/cocoapods-tt/native/gem_version.rb +5 -0
  22. data/lib/cocoapods-tt/native/generator/acknowledgements/markdown.rb +44 -0
  23. data/lib/cocoapods-tt/native/generator/acknowledgements/plist.rb +94 -0
  24. data/lib/cocoapods-tt/native/generator/acknowledgements.rb +107 -0
  25. data/lib/cocoapods-tt/native/generator/app_target_helper.rb +363 -0
  26. data/lib/cocoapods-tt/native/generator/bridge_support.rb +22 -0
  27. data/lib/cocoapods-tt/native/generator/constant.rb +19 -0
  28. data/lib/cocoapods-tt/native/generator/copy_dsyms_script.rb +56 -0
  29. data/lib/cocoapods-tt/native/generator/copy_resources_script.rb +223 -0
  30. data/lib/cocoapods-tt/native/generator/copy_xcframework_script.rb +227 -0
  31. data/lib/cocoapods-tt/native/generator/dummy_source.rb +31 -0
  32. data/lib/cocoapods-tt/native/generator/embed_frameworks_script.rb +196 -0
  33. data/lib/cocoapods-tt/native/generator/file_list.rb +39 -0
  34. data/lib/cocoapods-tt/native/generator/header.rb +103 -0
  35. data/lib/cocoapods-tt/native/generator/info_plist_file.rb +128 -0
  36. data/lib/cocoapods-tt/native/generator/module_map.rb +99 -0
  37. data/lib/cocoapods-tt/native/generator/prefix_header.rb +60 -0
  38. data/lib/cocoapods-tt/native/generator/script_phase_constants.rb +100 -0
  39. data/lib/cocoapods-tt/native/generator/umbrella_header.rb +46 -0
  40. data/lib/cocoapods-tt/native/hooks_manager.rb +132 -0
  41. data/lib/cocoapods-tt/native/installer/analyzer/analysis_result.rb +87 -0
  42. data/lib/cocoapods-tt/native/installer/analyzer/locking_dependency_analyzer.rb +103 -0
  43. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant.rb +87 -0
  44. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant_set.rb +175 -0
  45. data/lib/cocoapods-tt/native/installer/analyzer/podfile_dependency_cache.rb +55 -0
  46. data/lib/cocoapods-tt/native/installer/analyzer/sandbox_analyzer.rb +268 -0
  47. data/lib/cocoapods-tt/native/installer/analyzer/specs_state.rb +108 -0
  48. data/lib/cocoapods-tt/native/installer/analyzer/target_inspection_result.rb +58 -0
  49. data/lib/cocoapods-tt/native/installer/analyzer/target_inspector.rb +258 -0
  50. data/lib/cocoapods-tt/native/installer/analyzer.rb +1204 -0
  51. data/lib/cocoapods-tt/native/installer/base_install_hooks_context.rb +135 -0
  52. data/lib/cocoapods-tt/native/installer/installation_options.rb +195 -0
  53. data/lib/cocoapods-tt/native/installer/pod_source_installer.rb +224 -0
  54. data/lib/cocoapods-tt/native/installer/pod_source_preparer.rb +77 -0
  55. data/lib/cocoapods-tt/native/installer/podfile_validator.rb +168 -0
  56. data/lib/cocoapods-tt/native/installer/post_install_hooks_context.rb +9 -0
  57. data/lib/cocoapods-tt/native/installer/post_integrate_hooks_context.rb +9 -0
  58. data/lib/cocoapods-tt/native/installer/pre_install_hooks_context.rb +51 -0
  59. data/lib/cocoapods-tt/native/installer/pre_integrate_hooks_context.rb +9 -0
  60. data/lib/cocoapods-tt/native/installer/project_cache/project_cache.rb +11 -0
  61. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analysis_result.rb +53 -0
  62. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analyzer.rb +200 -0
  63. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_version.rb +43 -0
  64. data/lib/cocoapods-tt/native/installer/project_cache/project_installation_cache.rb +103 -0
  65. data/lib/cocoapods-tt/native/installer/project_cache/project_metadata_cache.rb +73 -0
  66. data/lib/cocoapods-tt/native/installer/project_cache/target_cache_key.rb +176 -0
  67. data/lib/cocoapods-tt/native/installer/project_cache/target_metadata.rb +74 -0
  68. data/lib/cocoapods-tt/native/installer/sandbox_dir_cleaner.rb +105 -0
  69. data/lib/cocoapods-tt/native/installer/sandbox_header_paths_installer.rb +45 -0
  70. data/lib/cocoapods-tt/native/installer/source_provider_hooks_context.rb +34 -0
  71. data/lib/cocoapods-tt/native/installer/target_uuid_generator.rb +34 -0
  72. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +179 -0
  73. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator.rb +815 -0
  74. data/lib/cocoapods-tt/native/installer/user_project_integrator.rb +280 -0
  75. data/lib/cocoapods-tt/native/installer/xcode/multi_pods_project_generator.rb +82 -0
  76. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb +66 -0
  77. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_installer.rb +192 -0
  78. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/app_host_installer.rb +154 -0
  79. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/file_references_installer.rb +329 -0
  80. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb +195 -0
  81. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_installer.rb +1239 -0
  82. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_integrator.rb +312 -0
  83. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pods_project_writer.rb +90 -0
  84. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/project_generator.rb +120 -0
  85. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installation_result.rb +140 -0
  86. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer.rb +257 -0
  87. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer_helper.rb +110 -0
  88. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator.rb +291 -0
  89. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator_result.rb +54 -0
  90. data/lib/cocoapods-tt/native/installer/xcode/single_pods_project_generator.rb +38 -0
  91. data/lib/cocoapods-tt/native/installer/xcode/target_validator.rb +170 -0
  92. data/lib/cocoapods-tt/native/installer/xcode.rb +11 -0
  93. data/lib/cocoapods-tt/native/installer.rb +1044 -0
  94. data/lib/cocoapods-tt/native/native_target_extension.rb +60 -0
  95. data/lib/cocoapods-tt/native/open-uri.rb +33 -0
  96. data/lib/cocoapods-tt/native/podfile.rb +13 -0
  97. data/lib/cocoapods-tt/native/project.rb +544 -0
  98. data/lib/cocoapods-tt/native/resolver/lazy_specification.rb +88 -0
  99. data/lib/cocoapods-tt/native/resolver/resolver_specification.rb +41 -0
  100. data/lib/cocoapods-tt/native/resolver.rb +600 -0
  101. data/lib/cocoapods-tt/native/sandbox/file_accessor.rb +532 -0
  102. data/lib/cocoapods-tt/native/sandbox/headers_store.rb +163 -0
  103. data/lib/cocoapods-tt/native/sandbox/path_list.rb +242 -0
  104. data/lib/cocoapods-tt/native/sandbox/pod_dir_cleaner.rb +71 -0
  105. data/lib/cocoapods-tt/native/sandbox/podspec_finder.rb +23 -0
  106. data/lib/cocoapods-tt/native/sandbox.rb +470 -0
  107. data/lib/cocoapods-tt/native/sources_manager.rb +221 -0
  108. data/lib/cocoapods-tt/native/target/aggregate_target.rb +558 -0
  109. data/lib/cocoapods-tt/native/target/build_settings.rb +1385 -0
  110. data/lib/cocoapods-tt/native/target/pod_target.rb +1168 -0
  111. data/lib/cocoapods-tt/native/target.rb +378 -0
  112. data/lib/cocoapods-tt/native/user_interface/error_report.rb +204 -0
  113. data/lib/cocoapods-tt/native/user_interface/inspector_reporter.rb +102 -0
  114. data/lib/cocoapods-tt/native/user_interface.rb +463 -0
  115. data/lib/cocoapods-tt/native/validator.rb +1170 -0
  116. data/lib/cocoapods-tt/native/version_metadata.rb +26 -0
  117. data/lib/cocoapods-tt/native/xcode/framework_paths.rb +54 -0
  118. data/lib/cocoapods-tt/native/xcode/linkage_analyzer.rb +22 -0
  119. data/lib/cocoapods-tt/native/xcode/xcframework/xcframework_slice.rb +138 -0
  120. data/lib/cocoapods-tt/native/xcode/xcframework.rb +99 -0
  121. data/lib/cocoapods-tt/native/xcode.rb +7 -0
  122. data/lib/cocoapods-tt.rb +1 -0
  123. data/lib/cocoapods_plugin.rb +17 -0
  124. metadata +193 -0
@@ -0,0 +1,1204 @@
1
+ require 'cocoapods/podfile'
2
+
3
+ module Pod
4
+ class Installer
5
+ # Analyzes the Podfile, the Lockfile, and the sandbox manifest to generate
6
+ # the information relative to a CocoaPods installation.
7
+ #
8
+ class Analyzer
9
+ include Config::Mixin
10
+
11
+ autoload :AnalysisResult, 'cocoapods/installer/analyzer/analysis_result'
12
+ autoload :LockingDependencyAnalyzer, 'cocoapods/installer/analyzer/locking_dependency_analyzer'
13
+ autoload :PodfileDependencyCache, 'cocoapods/installer/analyzer/podfile_dependency_cache'
14
+ autoload :PodVariant, 'cocoapods/installer/analyzer/pod_variant'
15
+ autoload :PodVariantSet, 'cocoapods/installer/analyzer/pod_variant_set'
16
+ autoload :SandboxAnalyzer, 'cocoapods/installer/analyzer/sandbox_analyzer'
17
+ autoload :SpecsState, 'cocoapods/installer/analyzer/specs_state'
18
+ autoload :TargetInspectionResult, 'cocoapods/installer/analyzer/target_inspection_result'
19
+ autoload :TargetInspector, 'cocoapods/installer/analyzer/target_inspector'
20
+
21
+ # @return [String] The version of iOS which requires binaries with only 64-bit architectures
22
+ #
23
+ IOS_64_BIT_ONLY_VERSION = Version.new('11.0')
24
+
25
+ # @return [Integer] The Xcode object version until which 64-bit architectures should be manually specified
26
+ #
27
+ # Xcode 10 will automatically select the correct architectures based on deployment target
28
+ IOS_64_BIT_ONLY_PROJECT_VERSION = 50
29
+
30
+ # @return [Sandbox] The sandbox to use for this analysis.
31
+ #
32
+ attr_reader :sandbox
33
+
34
+ # @return [Podfile] The Podfile specification that contains the information of the Pods that should be installed.
35
+ #
36
+ attr_reader :podfile
37
+
38
+ # @return [Lockfile, nil] The Lockfile, if available, that stores the information about the Pods previously installed.
39
+ #
40
+ attr_reader :lockfile
41
+
42
+ # @return [Array<Source>] Sources provided by plugins or `nil`.
43
+ #
44
+ attr_reader :plugin_sources
45
+
46
+ # @return [Bool] Whether the analysis has dependencies and thus sources must be configured.
47
+ #
48
+ # @note This is used by the `pod lib lint` command to prevent update of specs when not needed.
49
+ #
50
+ attr_reader :has_dependencies
51
+ alias_method :has_dependencies?, :has_dependencies
52
+
53
+ # @return [Hash, Boolean, nil] Pods that have been requested to be updated or true if all Pods should be updated.
54
+ # This can be false if no pods should be updated.
55
+ #
56
+ attr_reader :pods_to_update
57
+
58
+ # @return [InstallationOptions] the installation options specified by the Podfile
59
+ #
60
+ attr_reader :installation_options
61
+
62
+ # @return [Source::Manager] the sources manager to use when resolving dependencies
63
+ #
64
+ attr_reader :sources_manager
65
+
66
+ # Initialize a new instance
67
+ #
68
+ # @param [Sandbox] sandbox @see #sandbox
69
+ # @param [Podfile] podfile @see #podfile
70
+ # @param [Lockfile, nil] lockfile @see #lockfile
71
+ # @param [Array<Source>] plugin_sources @see #plugin_sources
72
+ # @param [Boolean] has_dependencies @see #has_dependencies
73
+ # @param [Hash, Boolean, nil] pods_to_update @see #pods_to_update
74
+ # @param [Source::Manager] sources_manager @see #sources_manager
75
+ #
76
+ def initialize(sandbox, podfile, lockfile = nil, plugin_sources = nil, has_dependencies = true,
77
+ pods_to_update = false, sources_manager = Source::Manager.new(config.repos_dir))
78
+ @sandbox = sandbox
79
+ @podfile = podfile
80
+ @lockfile = lockfile
81
+ @plugin_sources = plugin_sources
82
+ @has_dependencies = has_dependencies
83
+ @pods_to_update = pods_to_update
84
+ @installation_options = podfile.installation_options
85
+ @podfile_dependency_cache = PodfileDependencyCache.from_podfile(podfile)
86
+ @sources_manager = sources_manager
87
+ @result = nil
88
+ end
89
+
90
+ # Performs the analysis.
91
+ #
92
+ # The Podfile and the Lockfile provide the information necessary to
93
+ # compute which specification should be installed. The manifest of the
94
+ # sandbox returns which specifications are installed.
95
+ #
96
+ # @param [Bool] allow_fetches
97
+ # whether external sources may be fetched
98
+ #
99
+ # @return [AnalysisResult]
100
+ #
101
+ def analyze(allow_fetches = true)
102
+ return @result if @result
103
+ validate_podfile!
104
+ validate_lockfile_version!
105
+ if installation_options.integrate_targets?
106
+ target_inspections = inspect_targets_to_integrate
107
+ else
108
+ verify_platforms_specified!
109
+ target_inspections = {}
110
+ end
111
+ podfile_state = generate_podfile_state
112
+
113
+ store_existing_checkout_options
114
+ if allow_fetches == :outdated
115
+ # special-cased -- we're only really resolving for outdated, rather than doing a full analysis
116
+ elsif allow_fetches == true
117
+ fetch_external_sources(podfile_state)
118
+ elsif !dependencies_to_fetch(podfile_state).all?(&:local?)
119
+ raise Informative, 'Cannot analyze without fetching dependencies since the sandbox is not up-to-date. Run `pod install` to ensure all dependencies have been fetched.' \
120
+ "\n The missing dependencies are:\n \t#{dependencies_to_fetch(podfile_state).reject(&:local?).join("\n \t")}"
121
+ end
122
+
123
+ locked_dependencies = generate_version_locking_dependencies(podfile_state)
124
+ resolver_specs_by_target = resolve_dependencies(locked_dependencies)
125
+ validate_platforms(resolver_specs_by_target)
126
+ specifications = generate_specifications(resolver_specs_by_target)
127
+ aggregate_targets, pod_targets = generate_targets(resolver_specs_by_target, target_inspections)
128
+ sandbox_state = generate_sandbox_state(specifications)
129
+ specs_by_target = resolver_specs_by_target.each_with_object({}) do |rspecs_by_target, hash|
130
+ hash[rspecs_by_target[0]] = rspecs_by_target[1].map(&:spec)
131
+ end
132
+ specs_by_source = Hash[resolver_specs_by_target.values.flatten(1).group_by(&:source).map do |source, specs|
133
+ [source, specs.map(&:spec).uniq]
134
+ end]
135
+ sources.each { |s| specs_by_source[s] ||= [] }
136
+ @result = AnalysisResult.new(podfile_state, specs_by_target, specs_by_source, specifications, sandbox_state,
137
+ aggregate_targets, pod_targets, @podfile_dependency_cache)
138
+ end
139
+
140
+ # Updates the git source repositories.
141
+ #
142
+ def update_repositories
143
+ sources.each do |source|
144
+ if source.updateable?
145
+ sources_manager.update(source.name, true)
146
+ else
147
+ UI.message "Skipping `#{source.name}` update because the repository is not an updateable repository."
148
+ end
149
+ end
150
+ @specs_updated = true
151
+ end
152
+
153
+ # Returns the sources used to query for specifications.
154
+ #
155
+ # When no explicit Podfile sources or plugin sources are defined, this defaults to the master spec repository.
156
+ #
157
+ # @return [Array<Source>] the sources to be used in finding specifications, as specified by the podfile or all
158
+ # sources.
159
+ #
160
+ def sources
161
+ @sources ||= begin
162
+ sources = podfile.sources
163
+ plugin_sources = @plugin_sources || []
164
+
165
+ # Add any sources specified using the :source flag on individual dependencies.
166
+ dependency_sources = podfile_dependencies.map(&:podspec_repo).compact
167
+ all_dependencies_have_sources = dependency_sources.count == podfile_dependencies.count
168
+
169
+ if all_dependencies_have_sources
170
+ sources = dependency_sources
171
+ elsif has_dependencies? && sources.empty? && plugin_sources.empty?
172
+ sources = [Pod::TrunkSource::TRUNK_REPO_URL] + dependency_sources
173
+ else
174
+ sources += dependency_sources
175
+ end
176
+
177
+ result = sources.uniq.map do |source_url|
178
+ sources_manager.find_or_create_source_with_url(source_url)
179
+ end
180
+ unless plugin_sources.empty?
181
+ result.insert(0, *plugin_sources)
182
+ plugin_sources.each do |source|
183
+ sources_manager.add_source(source)
184
+ end
185
+ end
186
+ result
187
+ end
188
+ end
189
+
190
+ #-----------------------------------------------------------------------#
191
+
192
+ private
193
+
194
+ # @!group Configuration
195
+
196
+ # @return [Bool] Whether the version of the dependencies which did not
197
+ # change in the Podfile should be locked.
198
+ #
199
+ def update_mode?
200
+ pods_to_update != nil
201
+ end
202
+
203
+ # @return [Symbol] Whether and how the dependencies in the Podfile
204
+ # should be updated.
205
+ #
206
+ def update_mode
207
+ if !pods_to_update
208
+ :none
209
+ elsif pods_to_update == true
210
+ :all
211
+ elsif !pods_to_update[:pods].nil?
212
+ :selected
213
+ end
214
+ end
215
+
216
+ def podfile_dependencies
217
+ @podfile_dependency_cache.podfile_dependencies
218
+ end
219
+
220
+ #-----------------------------------------------------------------------#
221
+
222
+ def validate_podfile!
223
+ validator = Installer::PodfileValidator.new(podfile, @podfile_dependency_cache)
224
+ validator.validate
225
+
226
+ unless validator.valid?
227
+ raise Informative, validator.message
228
+ end
229
+ validator.warnings.uniq.each { |w| UI.warn(w) }
230
+ end
231
+
232
+ # @!group Analysis steps
233
+
234
+ # @note The warning about the version of the Lockfile doesn't use the
235
+ # `UI.warn` method because it prints the output only at the end
236
+ # of the installation. At that time CocoaPods could have crashed.
237
+ #
238
+ def validate_lockfile_version!
239
+ if lockfile && lockfile.cocoapods_version > Version.new(VERSION)
240
+ STDERR.puts '[!] The version of CocoaPods used to generate ' \
241
+ "the lockfile (#{lockfile.cocoapods_version}) is "\
242
+ "higher than the version of the current executable (#{VERSION}). " \
243
+ 'Incompatibility issues may arise.'.yellow
244
+ end
245
+ end
246
+
247
+ # Compares the {Podfile} with the {Lockfile} in order to detect which
248
+ # dependencies should be locked.
249
+ #
250
+ # @return [SpecsState] the states of the Podfile specs.
251
+ #
252
+ # @note As the target definitions share the same sandbox they should have
253
+ # the same version of a Pod. For this reason this method returns
254
+ # the name of the Pod (root name of the dependencies) and doesn't
255
+ # group them by target definition.
256
+ #
257
+ # @return [SpecState]
258
+ #
259
+ def generate_podfile_state
260
+ if lockfile
261
+ pods_state = nil
262
+ UI.section 'Finding Podfile changes' do
263
+ pods_by_state = lockfile.detect_changes_with_podfile(podfile)
264
+ pods_state = SpecsState.new(pods_by_state)
265
+ pods_state.print if config.verbose?
266
+ end
267
+ pods_state
268
+ else
269
+ state = SpecsState.new
270
+ state.added.merge(podfile_dependencies.map(&:root_name))
271
+ state
272
+ end
273
+ end
274
+
275
+ # Copies the pod targets of any of the app embedded aggregate targets into
276
+ # their potential host aggregate target, if that potential host aggregate target's
277
+ # user_target hosts any of the app embedded aggregate targets' user_targets
278
+ #
279
+ # @param [AggregateTarget] aggregate_target the aggregate target whose user_target
280
+ # might host one or more of the embedded aggregate targets' user_targets
281
+ #
282
+ # @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
283
+ # representing the embedded targets to be integrated
284
+ #
285
+ # @param [Boolean] libraries_only if true, only library-type embedded
286
+ # targets are considered, otherwise, all other types are have
287
+ # their pods copied to their host targets as well (extensions, etc.)
288
+ #
289
+ # @return [Hash{String=>Array<PodTarget>}] the additional pod targets to include to the host
290
+ # keyed by their configuration.
291
+ #
292
+ def embedded_target_pod_targets_by_host(aggregate_target, embedded_aggregate_targets, libraries_only)
293
+ return {} if aggregate_target.requires_host_target?
294
+ aggregate_user_target_uuids = Set.new(aggregate_target.user_targets.map(&:uuid))
295
+ embedded_pod_targets_by_build_config = Hash.new([].freeze)
296
+ embedded_aggregate_targets.each do |embedded_aggregate_target|
297
+ # Skip non libraries in library-only mode
298
+ next if libraries_only && !embedded_aggregate_target.library?
299
+ next if aggregate_target.search_paths_aggregate_targets.include?(embedded_aggregate_target)
300
+ next unless embedded_aggregate_target.user_targets.any? do |embedded_user_target|
301
+ # You have to ask the host target's project for the host targets of
302
+ # the embedded target, as opposed to asking user_project for the
303
+ # embedded targets of the host target. The latter doesn't work when
304
+ # the embedded target lives in a sub-project. The lines below get
305
+ # the host target uuids for the embedded target and checks to see if
306
+ # those match to any of the user_target uuids in the aggregate_target.
307
+ host_target_uuids = Set.new(aggregate_target.user_project.host_targets_for_embedded_target(embedded_user_target).map(&:uuid))
308
+ !aggregate_user_target_uuids.intersection(host_target_uuids).empty?
309
+ end
310
+ embedded_aggregate_target.user_build_configurations.each_key do |configuration_name|
311
+ pod_target_names = Set.new(aggregate_target.pod_targets_for_build_configuration(configuration_name).map(&:name))
312
+ embedded_pod_targets = embedded_aggregate_target.pod_targets_for_build_configuration(configuration_name).select do |pod_target|
313
+ if !pod_target_names.include?(pod_target.name) &&
314
+ aggregate_target.pod_targets.none? { |aggregate_pod_target| (pod_target.specs - aggregate_pod_target.specs).empty? } &&
315
+ (libraries_only || pod_target.build_as_dynamic?)
316
+ pod_target.name
317
+ end
318
+ end
319
+ embedded_pod_targets_by_build_config[configuration_name] += embedded_pod_targets
320
+ end
321
+ end
322
+ embedded_pod_targets_by_build_config
323
+ end
324
+
325
+ # Raises an error if there are embedded targets in the Podfile, but
326
+ # their host targets have not been declared in the Podfile. As it
327
+ # finds host targets, it collection information on host target types.
328
+ #
329
+ # @param [Array<AggregateTarget>] aggregate_targets the generated
330
+ # aggregate targets
331
+ #
332
+ # @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
333
+ # representing the embedded targets to be integrated
334
+ #
335
+ def analyze_host_targets_in_podfile(aggregate_targets, embedded_aggregate_targets)
336
+ target_definitions_by_uuid = {}
337
+ cli_host_with_dynamic_linkage = []
338
+ cli_product_type = 'com.apple.product-type.tool'
339
+ # Collect aggregate target definitions by uuid to later lookup host target
340
+ # definitions and verify their compatibility with their embedded targets
341
+ aggregate_targets.each do |target|
342
+ target.user_targets.each do |user_target|
343
+ target_definition = target.target_definition
344
+ target_definitions_by_uuid[user_target.uuid] = target_definition
345
+ if user_target.product_type == cli_product_type && target_definition.build_type.linkage == :dynamic
346
+ cli_host_with_dynamic_linkage << user_target
347
+ end
348
+ end
349
+ end
350
+ aggregate_target_user_projects = aggregate_targets.map(&:user_project)
351
+ embedded_targets_missing_hosts = []
352
+ host_uuid_to_embedded_target_definitions = {}
353
+ # Search all of the known user projects for each embedded target's hosts
354
+ embedded_aggregate_targets.each do |target|
355
+ host_uuids = aggregate_target_user_projects.product(target.user_targets).flat_map do |user_project, user_target|
356
+ user_project.host_targets_for_embedded_target(user_target).map(&:uuid)
357
+ end
358
+ # For each host, keep track of its embedded target definitions
359
+ # to later verify each embedded target's compatiblity with its host,
360
+ # ignoring the hosts that aren't known to CocoaPods (no target
361
+ # definitions in the Podfile)
362
+ host_uuids.each do |uuid|
363
+ (host_uuid_to_embedded_target_definitions[uuid] ||= []) << target.target_definition if target_definitions_by_uuid.key? uuid
364
+ end
365
+ # If none of the hosts are known to CocoaPods (no target definitions
366
+ # in the Podfile), add it to the list of targets missing hosts
367
+ embedded_targets_missing_hosts << target unless host_uuids.any? do |uuid|
368
+ target_definitions_by_uuid.key? uuid
369
+ end
370
+ end
371
+
372
+ unless cli_host_with_dynamic_linkage.empty?
373
+ UI.warn "The Podfile contains command line tool target(s) (#{cli_host_with_dynamic_linkage.map(&:name).to_sentence}) which are attempting to integrate dynamic frameworks or libraries." \
374
+ "\n" \
375
+ 'This may not behave as expected, because command line tools are usually distributed as a single binary and cannot contain their own dynamic dependencies.'
376
+ end
377
+
378
+ unless embedded_targets_missing_hosts.empty?
379
+ embedded_targets_missing_hosts_product_types = Set.new embedded_targets_missing_hosts.flat_map(&:user_targets).map(&:symbol_type)
380
+ target_names = embedded_targets_missing_hosts.map do |target|
381
+ target.name.sub('Pods-', '') # Make the target names more recognizable to the user
382
+ end.join ', '
383
+ # If the targets missing hosts are only frameworks, then this is likely
384
+ # a project for doing framework development. In that case, just warn that
385
+ # the frameworks that these targets depend on won't be integrated anywhere
386
+ if embedded_targets_missing_hosts_product_types.subset?(Set.new([:framework, :static_library]))
387
+ 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)." \
388
+ "\n" \
389
+ '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).'
390
+ else
391
+ raise Informative, "Unable to find host target(s) for #{target_names}. Please add the host targets for the embedded targets to the Podfile." \
392
+ "\n" \
393
+ '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:' \
394
+ "\n- Framework" \
395
+ "\n- App Extension" \
396
+ "\n- Watch OS 1 Extension" \
397
+ "\n- Messages Extension (except when used with a Messages Application)"
398
+ end
399
+ end
400
+
401
+ target_mismatches = []
402
+ host_uuid_to_embedded_target_definitions.each do |uuid, target_definitions|
403
+ host_target_definition = target_definitions_by_uuid[uuid]
404
+ target_definitions.each do |target_definition|
405
+ unless host_target_definition.uses_frameworks? == target_definition.uses_frameworks?
406
+ 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!."
407
+ end
408
+ end
409
+ end
410
+
411
+ unless target_mismatches.empty?
412
+ 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):'
413
+ raise Informative, heading + "\n\n" + target_mismatches.sort.uniq.join("\n")
414
+ end
415
+ end
416
+
417
+ # Creates the models that represent the targets generated by CocoaPods.
418
+ #
419
+ # @param [Hash{TargetDefinition => Array<ResolvedSpecification>}] resolver_specs_by_target
420
+ # mapping of targets to resolved specs (containing information about test usage)
421
+ # aggregate targets
422
+ #
423
+ # @param [Hash{TargetDefinition => TargetInspectionResult}] target_inspections
424
+ # the user target inspections used to construct the aggregate and pod targets.
425
+ #
426
+ # @return [(Array<AggregateTarget>, Array<PodTarget>)] the list of aggregate targets generated,
427
+ # and the list of pod targets generated.
428
+ #
429
+ def generate_targets(resolver_specs_by_target, target_inspections)
430
+ resolver_specs_by_target = resolver_specs_by_target.reject { |td, _| td.abstract? && !td.platform }
431
+ pod_targets = generate_pod_targets(resolver_specs_by_target, target_inspections)
432
+ pod_targets_by_target_definition = group_pod_targets_by_target_definition(pod_targets, resolver_specs_by_target)
433
+ aggregate_targets = resolver_specs_by_target.keys.reject(&:abstract?).map do |target_definition|
434
+ generate_aggregate_target(target_definition, target_inspections, pod_targets_by_target_definition)
435
+ end
436
+ aggregate_targets.each do |target|
437
+ search_paths_aggregate_targets = aggregate_targets.select do |aggregate_target|
438
+ target.target_definition.targets_to_inherit_search_paths.include?(aggregate_target.target_definition)
439
+ end
440
+ target.search_paths_aggregate_targets.concat(search_paths_aggregate_targets).freeze
441
+ end
442
+
443
+ aggregate_targets.each do |aggregate_target|
444
+ is_app_extension = !(aggregate_target.user_targets.map(&:symbol_type) &
445
+ [:app_extension, :watch_extension, :watch2_extension, :tv_extension, :messages_extension]).empty?
446
+ is_app_extension ||= aggregate_target.user_targets.any? do |user_target|
447
+ user_target.common_resolved_build_setting('APPLICATION_EXTENSION_API_ONLY', :resolve_against_xcconfig => true) == 'YES'
448
+ end
449
+ if is_app_extension
450
+ aggregate_target.mark_application_extension_api_only
451
+ aggregate_target.pod_targets.each(&:mark_application_extension_api_only)
452
+ end
453
+
454
+ build_library_for_distribution = aggregate_target.user_targets.any? do |user_target|
455
+ user_target.common_resolved_build_setting('BUILD_LIBRARY_FOR_DISTRIBUTION', :resolve_against_xcconfig => true) == 'YES'
456
+ end
457
+ if build_library_for_distribution
458
+ aggregate_target.mark_build_library_for_distribution
459
+ aggregate_target.pod_targets.each(&:mark_build_library_for_distribution)
460
+ end
461
+ end
462
+
463
+ if installation_options.integrate_targets?
464
+ # Copy embedded target pods that cannot have their pods embedded as frameworks to
465
+ # their host targets, and ensure we properly link library pods to their host targets
466
+ embedded_targets = aggregate_targets.select(&:requires_host_target?)
467
+ analyze_host_targets_in_podfile(aggregate_targets, embedded_targets)
468
+
469
+ use_frameworks_embedded_targets, non_use_frameworks_embedded_targets = embedded_targets.partition(&:build_as_framework?)
470
+ aggregate_targets = aggregate_targets.map do |aggregate_target|
471
+ # For targets that require dynamic frameworks, we always have to copy their pods to their
472
+ # host targets because those frameworks will all be loaded from the host target's bundle
473
+ embedded_pod_targets = embedded_target_pod_targets_by_host(aggregate_target, use_frameworks_embedded_targets, false)
474
+
475
+ # For targets that don't require dynamic frameworks, we only have to consider library-type
476
+ # targets because their host targets will still need to link their pods
477
+ embedded_pod_targets.merge!(embedded_target_pod_targets_by_host(aggregate_target, non_use_frameworks_embedded_targets, true))
478
+
479
+ next aggregate_target if embedded_pod_targets.empty?
480
+ aggregate_target.merge_embedded_pod_targets(embedded_pod_targets)
481
+ end
482
+ end
483
+ [aggregate_targets, pod_targets]
484
+ end
485
+
486
+ # Setup the aggregate target for a single user target
487
+ #
488
+ # @param [TargetDefinition] target_definition
489
+ # the target definition for the user target.
490
+ #
491
+ # @param [Hash{TargetDefinition => TargetInspectionResult}] target_inspections
492
+ # the user target inspections used to construct the aggregate and pod targets.
493
+ #
494
+ # @param [Hash{TargetDefinition => Array<PodTarget>}] pod_targets_by_target_definition
495
+ # the pod targets grouped by target.
496
+ #
497
+ # @return [AggregateTarget]
498
+ #
499
+ def generate_aggregate_target(target_definition, target_inspections, pod_targets_by_target_definition)
500
+ if installation_options.integrate_targets?
501
+ target_inspection = target_inspections[target_definition]
502
+ raise "missing inspection for #{target_definition.inspect}" unless target_inspection
503
+ target_requires_64_bit = Analyzer.requires_64_bit_archs?(target_definition.platform, target_inspection.project.object_version)
504
+ user_project = target_inspection.project
505
+ client_root = target_inspection.client_root
506
+ user_target_uuids = target_inspection.project_target_uuids
507
+ user_build_configurations = target_inspection.build_configurations
508
+ archs = target_requires_64_bit ? ['$(ARCHS_STANDARD_64_BIT)'] : target_inspection.archs
509
+ else
510
+ target_requires_64_bit = Analyzer.requires_64_bit_archs?(target_definition.platform, nil)
511
+ user_project = nil
512
+ client_root = config.installation_root.realpath
513
+ user_target_uuids = []
514
+ user_build_configurations = target_definition.build_configurations || Target::DEFAULT_BUILD_CONFIGURATIONS
515
+ archs = target_requires_64_bit ? ['$(ARCHS_STANDARD_64_BIT)'] : []
516
+ end
517
+ platform = target_definition.platform
518
+ build_configurations = user_build_configurations.keys.concat(target_definition.all_whitelisted_configurations).uniq
519
+ pod_targets_for_build_configuration = filter_pod_targets_for_target_definition(target_definition,
520
+ pod_targets_by_target_definition,
521
+ build_configurations)
522
+ build_type = target_definition.uses_frameworks? ? BuildType.static_framework : BuildType.static_library
523
+ AggregateTarget.new(sandbox, build_type, user_build_configurations, archs, platform, target_definition,
524
+ client_root, user_project, user_target_uuids, pod_targets_for_build_configuration)
525
+ end
526
+
527
+ # Returns a filtered list of pod targets that should or should not be part of the target definition. Pod targets
528
+ # used by tests only are filtered.
529
+ #
530
+ # @return [Hash{TargetDefinition => Array<PodTarget>}]
531
+ #
532
+ def group_pod_targets_by_target_definition(pod_targets, resolver_specs_by_target)
533
+ pod_targets_by_target_definition = Hash.new { |h, td| h[td] = [] }
534
+ pod_targets.each do |pod_target|
535
+ pod_target.target_definitions.each do |td|
536
+ pod_targets_by_target_definition[td] << pod_target
537
+ end
538
+ end
539
+ resolver_specs_by_target.each do |td, resolver_specs|
540
+ specs_by_pod_name = resolver_specs.group_by { |s| s.root.name }
541
+ specs_by_pod_name.reject! { |_, specs| specs.all?(&:used_by_non_library_targets_only?) }
542
+ pod_targets_by_target_definition[td].keep_if { |pod_target| specs_by_pod_name.key?(pod_target.pod_name) }
543
+ end
544
+
545
+ pod_targets_by_target_definition
546
+ end
547
+
548
+ # Returns a filtered list of pod targets that should or should not be part of the target definition. Pod targets
549
+ # used by tests only are filtered.
550
+ #
551
+ # @param [TargetDefinition] target_definition
552
+ # the target definition to use as the base for filtering
553
+ #
554
+ # @param [Hash{TargetDefinition => Array<PodTarget>}] pod_targets_by_target_definition
555
+ # the pod targets grouped by target.
556
+ #
557
+ # @param [Array<String>] build_configurations
558
+ # The list of all build configurations the targets will be built for.
559
+ #
560
+ # @return [Hash{String => Array<PodTarget>}]
561
+ # the filtered list of pod targets, grouped by build configuration.
562
+ #
563
+ def filter_pod_targets_for_target_definition(target_definition, pod_targets_by_target_definition,
564
+ build_configurations)
565
+ pod_targets_by_build_config = Hash.new([].freeze)
566
+ build_configurations.each { |config| pod_targets_by_build_config[config] = [] }
567
+
568
+ dependencies_by_root_name = @podfile_dependency_cache.target_definition_dependencies(target_definition).group_by(&:root_name)
569
+
570
+ pod_targets_by_target_definition[target_definition].each do |pod_target|
571
+ pod_name = pod_target.pod_name
572
+ dependencies = dependencies_by_root_name[pod_name] || []
573
+
574
+ build_configurations.each do |configuration_name|
575
+ whitelists = dependencies.map do |dependency|
576
+ target_definition.pod_whitelisted_for_configuration?(dependency.name, configuration_name)
577
+ end.uniq
578
+
579
+ case whitelists
580
+ when [], [true] then nil
581
+ when [false] then next
582
+ else
583
+ raise Informative, "The subspecs of `#{pod_name}` are linked to " \
584
+ "different build configurations for the `#{target_definition}` " \
585
+ 'target. CocoaPods does not currently support subspecs across ' \
586
+ 'different build configurations.'
587
+ end
588
+
589
+ pod_targets_by_build_config[configuration_name] << pod_target
590
+ end
591
+ end
592
+
593
+ pod_targets_by_build_config
594
+ end
595
+
596
+ # Setup the pod targets for an aggregate target. Deduplicates resulting
597
+ # targets by grouping by platform and subspec by their root
598
+ # to create a {PodTarget} for each spec.
599
+ #
600
+ # @param [Hash{TargetDefinition => Array<ResolvedSpecification>}] resolver_specs_by_target
601
+ # the resolved specifications grouped by target.
602
+ #
603
+ # @param [Hash{TargetDefinition => TargetInspectionResult}] target_inspections
604
+ # the user target inspections used to construct the aggregate and pod targets.
605
+ #
606
+ # @return [Array<PodTarget>]
607
+ #
608
+ def generate_pod_targets(resolver_specs_by_target, target_inspections)
609
+ if installation_options.deduplicate_targets?
610
+ distinct_targets = resolver_specs_by_target.each_with_object({}) do |dependency, hash|
611
+ target_definition, dependent_specs = *dependency
612
+ dependent_specs.group_by(&:root).each do |root_spec, resolver_specs|
613
+ all_specs = resolver_specs.map(&:spec)
614
+ all_specs_by_type = all_specs.group_by(&:spec_type)
615
+ library_specs = all_specs_by_type[:library] || []
616
+ test_specs = all_specs_by_type[:test] || []
617
+ app_specs = all_specs_by_type[:app] || []
618
+ build_type = determine_build_type(root_spec, target_definition.build_type)
619
+ pod_variant = PodVariant.new(library_specs, test_specs, app_specs, target_definition.platform, build_type)
620
+ hash[root_spec] ||= {}
621
+ (hash[root_spec][pod_variant] ||= []) << target_definition
622
+ pod_variant_spec = hash[root_spec].keys.find { |k| k == pod_variant }
623
+ pod_variant_spec.test_specs.concat(test_specs).uniq!
624
+ pod_variant_spec.app_specs.concat(app_specs).uniq!
625
+ end
626
+ end
627
+
628
+ # Remap pod variants to a new instance that includes the Swift version since we now have the full set
629
+ # of target definitions.
630
+ distinct_targets = Hash[distinct_targets.map do |root, target_definitions_by_variant|
631
+ variants = Hash[target_definitions_by_variant.map do |variant, target_definitions|
632
+ swift_version = determine_swift_version(variant.root_spec, target_definitions)
633
+ [variant.scoped_with_swift_version(swift_version), target_definitions]
634
+ end]
635
+ [root, variants]
636
+ end]
637
+
638
+ pod_targets = distinct_targets.flat_map do |_root, target_definitions_by_variant|
639
+ target_definitions_by_variant.each_value do |target_definitions|
640
+ target_definitions.reject!(&:abstract?) unless target_definitions.all?(&:abstract?)
641
+ end
642
+ suffixes = PodVariantSet.new(target_definitions_by_variant.keys).scope_suffixes
643
+ target_definitions_by_variant.map do |variant, target_definitions|
644
+ all_specs = variant.specs + variant.test_specs + variant.app_specs
645
+ generate_pod_target(target_definitions, variant.build_type, target_inspections, all_specs,
646
+ :scope_suffix => suffixes[variant], :swift_version => variant.swift_version)
647
+ end
648
+ end
649
+
650
+ all_specs = resolver_specs_by_target.values.flatten.map(&:spec).uniq.group_by(&:name)
651
+ compute_pod_target_dependencies(pod_targets, all_specs)
652
+ else
653
+ dedupe_cache = {}
654
+ resolver_specs_by_target.flat_map do |target_definition, specs|
655
+ grouped_specs = specs.group_by(&:root).values.uniq
656
+ pod_targets = grouped_specs.flat_map do |pod_specs|
657
+ build_type = determine_build_type(pod_specs.first.spec, target_definition.build_type)
658
+ swift_version = determine_swift_version(pod_specs.first.spec, [target_definition])
659
+ generate_pod_target([target_definition], build_type, target_inspections, pod_specs.map(&:spec),
660
+ :swift_version => swift_version).scoped(dedupe_cache)
661
+ end
662
+
663
+ compute_pod_target_dependencies(pod_targets, specs.map(&:spec).group_by(&:name))
664
+ end
665
+ end
666
+ end
667
+
668
+ # Compute the dependencies for the set of pod targets.
669
+ #
670
+ # @param [Array<PodTarget>] pod_targets
671
+ # pod targets.
672
+ #
673
+ # @param [Hash{String => Array<Specification>}] all_specs
674
+ # specifications grouped by name.
675
+ #
676
+ # @return [Array<PodTarget>]
677
+ #
678
+ def compute_pod_target_dependencies(pod_targets, all_specs)
679
+ pod_targets_by_name = pod_targets.group_by(&:pod_name).each_with_object({}) do |(name, values), hash|
680
+ # Sort the target by the number of activated subspecs, so that
681
+ # we prefer a minimal target as transitive dependency.
682
+ hash[name] = values.sort_by { |pt| pt.specs.count }
683
+ end
684
+
685
+ pod_targets.each do |target|
686
+ dependencies_by_config = dependencies_for_specs(target.library_specs, target.platform, all_specs)
687
+ target.dependent_targets_by_config = Hash[dependencies_by_config.map { |k, v| [k, filter_dependencies(v, pod_targets_by_name, target)] }]
688
+
689
+ target.test_dependent_targets_by_spec_name_by_config = target.test_specs.each_with_object({}) do |test_spec, hash|
690
+ test_dependencies_by_config = dependencies_for_specs([test_spec], target.platform, all_specs)
691
+ test_dependencies_by_config.each { |config, deps| deps.delete_if { |k, _| dependencies_by_config[config].key? k } }
692
+ hash[test_spec.name] = Hash[test_dependencies_by_config.map { |k, v| [k, filter_dependencies(v, pod_targets_by_name, target)] }]
693
+ end
694
+
695
+ target.app_dependent_targets_by_spec_name_by_config = target.app_specs.each_with_object({}) do |app_spec, hash|
696
+ app_dependencies_by_config = dependencies_for_specs([app_spec], target.platform, all_specs)
697
+ app_dependencies_by_config.each { |config, deps| deps.delete_if { |k, _| dependencies_by_config[config].key? k } }
698
+ hash[app_spec.name] = Hash[app_dependencies_by_config.map { |k, v| [k, filter_dependencies(v, pod_targets_by_name, target)] }]
699
+ end
700
+
701
+ target.test_app_hosts_by_spec = target.test_specs.each_with_object({}) do |test_spec, hash|
702
+ next unless app_host_name = test_spec.consumer(target.platform).app_host_name
703
+ app_host_spec = pod_targets_by_name[Specification.root_name(app_host_name)].flat_map(&:app_specs).find do |pt|
704
+ pt.name == app_host_name
705
+ end
706
+ app_host_dependencies = { app_host_spec.root => [app_host_spec] }
707
+ hash[test_spec] = [app_host_spec, filter_dependencies(app_host_dependencies, pod_targets_by_name, target).first]
708
+ end
709
+ end
710
+ end
711
+
712
+ def filter_dependencies(dependencies, pod_targets_by_name, target)
713
+ dependencies.map do |root_spec, deps|
714
+ pod_targets_by_name[root_spec.name].find do |t|
715
+ next false if t.platform.symbolic_name != target.platform.symbolic_name ||
716
+ # In the case of variants we must ensure that the platform this target is meant for is the same
717
+ # as the one we are interested in.
718
+ t.target_definitions.first.platform != target.target_definitions.first.platform ||
719
+ # rather than target type or requires_frameworks? since we want to group by what was specified in that
720
+ # _target definition_.
721
+ t.build_as_framework? != target.build_as_framework?
722
+ spec_names = t.specs.map(&:name)
723
+ deps.all? { |dep| spec_names.include?(dep.name) }
724
+ end
725
+ end
726
+ end
727
+
728
+ # Returns the specs upon which the given specs _directly_ depend.
729
+ #
730
+ # @note: This is implemented in the analyzer, because we don't have to
731
+ # care about the requirements after dependency resolution.
732
+ #
733
+ # @param [Array<Specification>] specs
734
+ # The specs, whose dependencies should be returned.
735
+ #
736
+ # @param [Platform] platform
737
+ # The platform for which the dependencies should be returned.
738
+ #
739
+ # @param [Hash{String => Array<Specification>}] all_specs
740
+ # All specifications which are installed alongside.
741
+ #
742
+ # @return [Hash{Symbol => Set<Specification>}]
743
+ #
744
+ def dependencies_for_specs(specs, platform, all_specs)
745
+ dependent_specs = {
746
+ :debug => Set.new,
747
+ :release => Set.new,
748
+ }
749
+
750
+ if !specs.empty? && !all_specs.empty?
751
+ specs.each do |s|
752
+ s.dependencies(platform).each do |dep|
753
+ all_specs[dep.name].each do |spec|
754
+ if spec.non_library_specification?
755
+ if s.test_specification? && spec.name == s.consumer(platform).app_host_name && spec.app_specification?
756
+ # This needs to be handled separately, since we _don't_ want to treat this as a "normal" dependency
757
+ next
758
+ end
759
+ raise Informative, "`#{s}` depends upon `#{spec}`, which is a `#{spec.spec_type}` spec."
760
+ end
761
+
762
+ dependent_specs.each do |config, set|
763
+ next unless s.dependency_whitelisted_for_configuration?(dep, config)
764
+ set << spec
765
+ end
766
+ end
767
+ end
768
+ end
769
+ end
770
+
771
+ Hash[dependent_specs.map { |k, v| [k, (v - specs).group_by(&:root)] }].freeze
772
+ end
773
+
774
+ # Create a target for each spec group
775
+ #
776
+ # @param [Array<TargetDefinition>] target_definitions
777
+ # the target definitions of the pod target
778
+ #
779
+ # @param [BuildType] build_type
780
+ # the BuildType to use for this pod target.
781
+ #
782
+ # @param [Hash{TargetDefinition => TargetInspectionResult}] target_inspections
783
+ # the user target inspections used to construct the aggregate and pod targets.
784
+ #
785
+ # @param [Array<Specification>] specs
786
+ # the specifications of an equal root.
787
+ #
788
+ # @param [String] scope_suffix
789
+ # @see PodTarget#scope_suffix
790
+ #
791
+ # @param [String] swift_version
792
+ # @see PodTarget#swift_version
793
+ #
794
+ # @return [PodTarget]
795
+ #
796
+ def generate_pod_target(target_definitions, build_type, target_inspections, specs, scope_suffix: nil,
797
+ swift_version: nil)
798
+ target_inspections = target_inspections.select { |t, _| target_definitions.include?(t) }.values
799
+ object_version = target_inspections.map { |ti| ti.project.object_version }.min
800
+ target_requires_64_bit = target_definitions.all? { |td| Analyzer.requires_64_bit_archs?(td.platform, object_version) }
801
+ if !target_inspections.empty?
802
+ user_build_configurations = target_inspections.map(&:build_configurations).reduce({}, &:merge)
803
+ archs = if target_requires_64_bit
804
+ ['$(ARCHS_STANDARD_64_BIT)']
805
+ else
806
+ target_inspections.flat_map(&:archs).compact.uniq.sort
807
+ end
808
+ else
809
+ user_build_configurations = Target::DEFAULT_BUILD_CONFIGURATIONS.merge(
810
+ target_definitions.map { |td| td.build_configurations || {} }.reduce({}, &:merge),
811
+ )
812
+ archs = target_requires_64_bit ? ['$(ARCHS_STANDARD_64_BIT)'] : []
813
+ end
814
+ platform = determine_platform(specs, target_definitions, build_type)
815
+ file_accessors = create_file_accessors(specs, platform)
816
+ PodTarget.new(sandbox, build_type, user_build_configurations, archs, platform, specs, target_definitions,
817
+ file_accessors, scope_suffix, swift_version)
818
+ end
819
+
820
+ # Creates the file accessors for a given pod.
821
+ #
822
+ # @param [Array<Specification>] specs
823
+ # the specs to map each file accessor to.
824
+ #
825
+ # @param [Platform] platform
826
+ # the platform to use when generating each file accessor.
827
+ #
828
+ # @return [Array<FileAccessor>]
829
+ #
830
+ def create_file_accessors(specs, platform)
831
+ name = specs.first.name
832
+ pod_root = sandbox.pod_dir(name)
833
+ path_list = Sandbox::PathList.new(pod_root)
834
+ specs.map do |spec|
835
+ Sandbox::FileAccessor.new(path_list, spec.consumer(platform))
836
+ end
837
+ end
838
+
839
+ # Calculates and returns the platform to use for the given list specs and target definitions.
840
+ #
841
+ # @note The platform is only determined by all library specs and ignores non library ones. Subspecs are always
842
+ # integrated in the same target as the root spec therefore the max deployment target is always returned
843
+ # across the specs passed.
844
+ #
845
+ # @param [Array<Specification>] specs
846
+ # the specs to inspect and calculate the platform for.
847
+ #
848
+ # @param [Array<TargetDefinition>] target_definitions
849
+ # the target definitions these specs are part of.
850
+ #
851
+ # @param [BuildType] build_type
852
+ # the #BuildType used for calculating the platform.
853
+ #
854
+ # @return [Platform]
855
+ #
856
+ def determine_platform(specs, target_definitions, build_type)
857
+ library_specs = specs.select(&:library_specification?)
858
+ platform_name = target_definitions.first.platform.name
859
+ default = Podfile::TargetDefinition::PLATFORM_DEFAULTS[platform_name]
860
+ deployment_target = library_specs.map do |library_spec|
861
+ Version.new(library_spec.deployment_target(platform_name) || default)
862
+ end.max
863
+ if platform_name == :ios && build_type.framework?
864
+ minimum = Version.new('8.0')
865
+ deployment_target = [deployment_target, minimum].max
866
+ end
867
+ Platform.new(platform_name, deployment_target)
868
+ end
869
+
870
+ # Determines the Swift version for the given spec within a list of target definitions. If the pod author has
871
+ # provided a set of Swift versions supported by their pod then the max Swift version is chosen, unless the target
872
+ # definitions specify explicit requirements for supported Swift versions. Otherwise the Swift version is derived
873
+ # by the target definitions that integrate this pod.
874
+ #
875
+ # @param [Specification] spec
876
+ # the specs to inspect and determine what Swift version to use.
877
+ #
878
+ # @param [Array<TargetDefinition>] target_definitions
879
+ # the target definitions the spec is part of.
880
+ #
881
+ # @return [String, nil] the computed Swift version or `nil` if the Swift version could not be determined.
882
+ #
883
+ def determine_swift_version(spec, target_definitions)
884
+ if spec.swift_versions.empty?
885
+ target_definitions.map(&:swift_version).compact.uniq.first
886
+ else
887
+ spec.swift_versions.sort.reverse_each.find do |swift_version|
888
+ target_definitions.all? do |td|
889
+ td.supports_swift_version?(swift_version)
890
+ end
891
+ end.to_s
892
+ end
893
+ end
894
+
895
+ # Calculates and returns the #BuildType to use for the given spec. If the spec specifies `static_framework` then
896
+ # it is honored as long as the host #BuildType also requires its pods to be integrated as frameworks.
897
+ #
898
+ # @param [Specification] spec
899
+ # the spec to determine the #BuildType for.
900
+ #
901
+ # @param [BuildType] target_definition_build_type
902
+ # The desired #BuildType by the target definition that integrates this target. If the pod target spec does
903
+ # not specify explicitly a `static_framework` #BuildType then the one from the target definition is used.
904
+ #
905
+ # @return [BuildType]
906
+ #
907
+ def determine_build_type(spec, target_definition_build_type)
908
+ if target_definition_build_type.framework?
909
+ root_spec = spec.root
910
+ root_spec.static_framework ? BuildType.static_framework : target_definition_build_type
911
+ else
912
+ BuildType.static_library
913
+ end
914
+ end
915
+
916
+ # Generates dependencies that require the specific version of the Pods
917
+ # that haven't changed in the {Lockfile}.
918
+ #
919
+ # These dependencies are passed to the {Resolver}, unless the installer
920
+ # is in update mode, to prevent it from upgrading the Pods that weren't
921
+ # changed in the {Podfile}.
922
+ #
923
+ # @param [SpecState] podfile_state
924
+ # the state of the podfile for which dependencies have or have not changed, added, deleted or updated.
925
+ #
926
+ # @return [Molinillo::DependencyGraph<Dependency>] the dependencies
927
+ # generated by the lockfile that prevent the resolver to update
928
+ # a Pod.
929
+ #
930
+ def generate_version_locking_dependencies(podfile_state)
931
+ if update_mode == :all || !lockfile
932
+ LockingDependencyAnalyzer.unlocked_dependency_graph
933
+ else
934
+ deleted_and_changed = podfile_state.changed + podfile_state.deleted
935
+ deleted_and_changed += pods_to_update[:pods] if update_mode == :selected
936
+ local_pod_names = podfile_dependencies.select(&:local?).map(&:root_name)
937
+ pods_to_unlock = local_pod_names.to_set.delete_if do |pod_name|
938
+ next unless sandbox_specification = sandbox.specification(pod_name)
939
+ sandbox_specification.checksum == lockfile.checksum(pod_name)
940
+ end
941
+ LockingDependencyAnalyzer.generate_version_locking_dependencies(lockfile, deleted_and_changed, pods_to_unlock)
942
+ end
943
+ end
944
+
945
+ # Fetches the podspecs of external sources if modifications to the
946
+ # sandbox are allowed.
947
+ #
948
+ # @note In update mode all the external sources are refreshed while in
949
+ # normal mode they are refreshed only if added or changed in the
950
+ # Podfile. Moreover, in normal specifications for unchanged Pods
951
+ # which are missing or are generated from an local source are
952
+ # fetched as well.
953
+ #
954
+ # @note It is possible to perform this step before the resolution
955
+ # process because external sources identify a single specific
956
+ # version (checkout). If the other dependencies are not
957
+ # compatible with the version reported by the podspec of the
958
+ # external source the resolver will raise.
959
+ #
960
+ # @param [SpecState] podfile_state
961
+ # the state of the podfile for which dependencies have or have not changed, added, deleted or updated.
962
+ #
963
+ # @return [void]
964
+ #
965
+ def fetch_external_sources(podfile_state)
966
+ verify_no_pods_with_different_sources!
967
+ deps = dependencies_to_fetch(podfile_state)
968
+ pods = pods_to_fetch(podfile_state)
969
+ return if deps.empty?
970
+ UI.section 'Fetching external sources' do
971
+ deps.sort.each do |dependency|
972
+ fetch_external_source(dependency, !pods.include?(dependency.root_name))
973
+ end
974
+ end
975
+ end
976
+
977
+ def verify_no_pods_with_different_sources!
978
+ deps_with_different_sources = podfile_dependencies.group_by(&:root_name).
979
+ select { |_root_name, dependencies| dependencies.map(&:external_source).uniq.count > 1 }
980
+ deps_with_different_sources.each do |root_name, dependencies|
981
+ raise Informative, 'There are multiple dependencies with different ' \
982
+ "sources for `#{root_name}` in #{UI.path podfile.defined_in_file}:" \
983
+ "\n\n- #{dependencies.map(&:to_s).join("\n- ")}"
984
+ end
985
+ end
986
+
987
+ def fetch_external_source(dependency, use_lockfile_options)
988
+ source = if use_lockfile_options && lockfile && checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name)
989
+ ExternalSources.from_params(checkout_options, dependency, podfile.defined_in_file, installation_options.clean?)
990
+ else
991
+ ExternalSources.from_dependency(dependency, podfile.defined_in_file, installation_options.clean?)
992
+ end
993
+ source.fetch(sandbox)
994
+ end
995
+
996
+ def dependencies_to_fetch(podfile_state)
997
+ @deps_to_fetch ||= begin
998
+ deps_to_fetch = []
999
+ deps_with_external_source = podfile_dependencies.select(&:external_source)
1000
+
1001
+ if update_mode == :all
1002
+ deps_to_fetch = deps_with_external_source
1003
+ else
1004
+ deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch(podfile_state).include?(dep.root_name) }
1005
+ deps_to_fetch_if_needed = deps_with_external_source.select { |dep| podfile_state.unchanged.include?(dep.root_name) }
1006
+ deps_to_fetch += deps_to_fetch_if_needed.select do |dep|
1007
+ sandbox.specification_path(dep.root_name).nil? ||
1008
+ !dep.external_source[:path].nil? ||
1009
+ !sandbox.pod_dir(dep.root_name).directory? ||
1010
+ checkout_requires_update?(dep)
1011
+ end
1012
+ end
1013
+ deps_to_fetch.uniq(&:root_name)
1014
+ end
1015
+ end
1016
+
1017
+ def checkout_requires_update?(dependency)
1018
+ return true unless lockfile && sandbox.manifest
1019
+ locked_checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name)
1020
+ sandbox_checkout_options = sandbox.manifest.checkout_options_for_pod_named(dependency.root_name)
1021
+ locked_checkout_options != sandbox_checkout_options
1022
+ end
1023
+
1024
+ def pods_to_fetch(podfile_state)
1025
+ @pods_to_fetch ||= begin
1026
+ pods_to_fetch = podfile_state.added + podfile_state.changed
1027
+ if update_mode == :selected
1028
+ pods_to_fetch += pods_to_update[:pods]
1029
+ elsif update_mode == :all
1030
+ pods_to_fetch += podfile_state.unchanged + podfile_state.deleted
1031
+ end
1032
+ pods_to_fetch += podfile_dependencies.
1033
+ select { |dep| Hash(dep.external_source).key?(:podspec) && sandbox.specification_path(dep.root_name).nil? }.
1034
+ map(&:root_name)
1035
+ pods_to_fetch
1036
+ end
1037
+ end
1038
+
1039
+ def store_existing_checkout_options
1040
+ return unless lockfile
1041
+ podfile_dependencies.select(&:external_source).each do |dep|
1042
+ if checkout_options = lockfile.checkout_options_for_pod_named(dep.root_name)
1043
+ sandbox.store_checkout_source(dep.root_name, checkout_options)
1044
+ end
1045
+ end
1046
+ end
1047
+
1048
+ # Converts the Podfile in a list of specifications grouped by target.
1049
+ #
1050
+ # @note As some dependencies might have external sources the resolver
1051
+ # is aware of the {Sandbox} and interacts with it to download the
1052
+ # podspecs of the external sources. This is necessary because the
1053
+ # resolver needs their specifications to analyze their
1054
+ # dependencies.
1055
+ #
1056
+ # @note The specifications of the external sources which are added,
1057
+ # modified or removed need to deleted from the sandbox before the
1058
+ # resolution process. Otherwise the resolver might use an
1059
+ # incorrect specification instead of pre-downloading it.
1060
+ #
1061
+ # @note In update mode the resolver is set to always update the specs
1062
+ # from external sources.
1063
+ #
1064
+ # @return [Hash{TargetDefinition => Array<Spec>}] the specifications
1065
+ # grouped by target.
1066
+ #
1067
+ def resolve_dependencies(locked_dependencies)
1068
+ duplicate_dependencies = podfile_dependencies.group_by(&:name).
1069
+ select { |_name, dependencies| dependencies.count > 1 }
1070
+ duplicate_dependencies.each do |name, dependencies|
1071
+ UI.warn "There are duplicate dependencies on `#{name}` in #{UI.path podfile.defined_in_file}:\n\n" \
1072
+ "- #{dependencies.map(&:to_s).join("\n- ")}"
1073
+ end
1074
+
1075
+ resolver_specs_by_target = nil
1076
+ UI.section "Resolving dependencies of #{UI.path(podfile.defined_in_file) || 'Podfile'}" do
1077
+ resolver = Pod::Resolver.new(sandbox, podfile, locked_dependencies, sources, @specs_updated, :sources_manager => sources_manager)
1078
+ resolver_specs_by_target = resolver.resolve
1079
+ resolver_specs_by_target.values.flatten(1).map(&:spec).each(&:validate_cocoapods_version)
1080
+ end
1081
+ resolver_specs_by_target
1082
+ end
1083
+
1084
+ # Warns for any specification that is incompatible with its target.
1085
+ #
1086
+ # @param [Hash{TargetDefinition => Array<Specification>}] resolver_specs_by_target
1087
+ # the resolved specifications grouped by target.
1088
+ #
1089
+ def validate_platforms(resolver_specs_by_target)
1090
+ resolver_specs_by_target.each do |target, specs|
1091
+ specs.map(&:spec).each do |spec|
1092
+ next unless target_platform = target.platform
1093
+ unless spec.available_platforms.any? { |p| target_platform.supports?(p) }
1094
+ UI.warn "The platform of the target `#{target.name}` " \
1095
+ "(#{target.platform}) may not be compatible with `#{spec}` which has " \
1096
+ "a minimum requirement of #{spec.available_platforms.join(' - ')}."
1097
+ end
1098
+ end
1099
+ end
1100
+ end
1101
+
1102
+ # Returns the list of all the resolved specifications.
1103
+ #
1104
+ # @param [Hash{TargetDefinition => Array<Specification>}] resolver_specs_by_target
1105
+ # the resolved specifications grouped by target.
1106
+ #
1107
+ # @return [Array<Specification>] the list of the specifications.
1108
+ #
1109
+ def generate_specifications(resolver_specs_by_target)
1110
+ resolver_specs_by_target.values.flatten.map(&:spec).uniq
1111
+ end
1112
+
1113
+ # Computes the state of the sandbox respect to the resolved
1114
+ # specifications.
1115
+ #
1116
+ # @return [SpecsState] the representation of the state of the manifest
1117
+ # specifications.
1118
+ #
1119
+ def generate_sandbox_state(specifications)
1120
+ sandbox_state = nil
1121
+ UI.section 'Comparing resolved specification to the sandbox manifest' do
1122
+ sandbox_analyzer = SandboxAnalyzer.new(sandbox, podfile, specifications, update_mode?)
1123
+ sandbox_state = sandbox_analyzer.analyze
1124
+ sandbox_state.print
1125
+ end
1126
+ sandbox_state
1127
+ end
1128
+
1129
+ class << self
1130
+ # @param [Platform] platform
1131
+ # The platform to build against
1132
+ #
1133
+ # @param [String, Nil] object_version
1134
+ # The user project's object version, or nil if not available
1135
+ #
1136
+ # @return [Boolean] Whether the platform requires 64-bit architectures
1137
+ #
1138
+ def requires_64_bit_archs?(platform, object_version)
1139
+ return false unless platform
1140
+ case platform.name
1141
+ when :osx
1142
+ true
1143
+ when :ios
1144
+ if (version = object_version)
1145
+ platform.deployment_target >= IOS_64_BIT_ONLY_VERSION && version.to_i < IOS_64_BIT_ONLY_PROJECT_VERSION
1146
+ else
1147
+ platform.deployment_target >= IOS_64_BIT_ONLY_VERSION
1148
+ end
1149
+ when :watchos
1150
+ false
1151
+ when :tvos
1152
+ false
1153
+ end
1154
+ end
1155
+ end
1156
+
1157
+ #-----------------------------------------------------------------------#
1158
+
1159
+ # @!group Analysis sub-steps
1160
+
1161
+ # Checks whether the platform is specified if not integrating
1162
+ #
1163
+ # @return [void]
1164
+ #
1165
+ def verify_platforms_specified!
1166
+ return if installation_options.integrate_targets?
1167
+ @podfile_dependency_cache.target_definition_list.each do |target_definition|
1168
+ if !target_definition.empty? && target_definition.platform.nil?
1169
+ raise Informative, 'It is necessary to specify the platform in the Podfile if not integrating.'
1170
+ end
1171
+ end
1172
+ end
1173
+
1174
+ # Precompute information for each target_definition in the Podfile
1175
+ #
1176
+ # @note The platforms are computed and added to each target_definition
1177
+ # because it might be necessary to infer the platform from the
1178
+ # user targets.
1179
+ #
1180
+ # @return [Hash{TargetDefinition => TargetInspectionResult}]
1181
+ #
1182
+ def inspect_targets_to_integrate
1183
+ inspection_result = {}
1184
+ UI.section 'Inspecting targets to integrate' do
1185
+ inspectors = @podfile_dependency_cache.target_definition_list.map do |target_definition|
1186
+ next if target_definition.abstract?
1187
+ TargetInspector.new(target_definition, config.installation_root)
1188
+ end.compact
1189
+ inspectors.group_by(&:compute_project_path).each do |project_path, target_inspectors|
1190
+ project = Xcodeproj::Project.open(project_path)
1191
+ target_inspectors.each do |inspector|
1192
+ target_definition = inspector.target_definition
1193
+ results = inspector.compute_results(project)
1194
+ inspection_result[target_definition] = results
1195
+ UI.message('Using `ARCHS` setting to build architectures of ' \
1196
+ "target `#{target_definition.label}`: (`#{results.archs.join('`, `')}`)")
1197
+ end
1198
+ end
1199
+ end
1200
+ inspection_result
1201
+ end
1202
+ end
1203
+ end
1204
+ end