xcocoapods 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6303 -0
- data/LICENSE +28 -0
- data/README.md +80 -0
- data/bin/pod +56 -0
- data/bin/sandbox-pod +168 -0
- data/lib/cocoapods.rb +73 -0
- data/lib/cocoapods/command.rb +175 -0
- data/lib/cocoapods/command/cache.rb +28 -0
- data/lib/cocoapods/command/cache/clean.rb +90 -0
- data/lib/cocoapods/command/cache/list.rb +69 -0
- data/lib/cocoapods/command/env.rb +66 -0
- data/lib/cocoapods/command/init.rb +128 -0
- data/lib/cocoapods/command/install.rb +45 -0
- data/lib/cocoapods/command/ipc.rb +19 -0
- data/lib/cocoapods/command/ipc/list.rb +40 -0
- data/lib/cocoapods/command/ipc/podfile.rb +31 -0
- data/lib/cocoapods/command/ipc/podfile_json.rb +30 -0
- data/lib/cocoapods/command/ipc/repl.rb +51 -0
- data/lib/cocoapods/command/ipc/spec.rb +29 -0
- data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
- data/lib/cocoapods/command/lib.rb +11 -0
- data/lib/cocoapods/command/lib/create.rb +105 -0
- data/lib/cocoapods/command/lib/lint.rb +121 -0
- data/lib/cocoapods/command/list.rb +39 -0
- data/lib/cocoapods/command/options/project_directory.rb +36 -0
- data/lib/cocoapods/command/options/repo_update.rb +34 -0
- data/lib/cocoapods/command/outdated.rb +140 -0
- data/lib/cocoapods/command/repo.rb +29 -0
- data/lib/cocoapods/command/repo/add.rb +103 -0
- data/lib/cocoapods/command/repo/lint.rb +82 -0
- data/lib/cocoapods/command/repo/list.rb +93 -0
- data/lib/cocoapods/command/repo/push.rb +281 -0
- data/lib/cocoapods/command/repo/remove.rb +36 -0
- data/lib/cocoapods/command/repo/update.rb +28 -0
- data/lib/cocoapods/command/setup.rb +103 -0
- data/lib/cocoapods/command/spec.rb +112 -0
- data/lib/cocoapods/command/spec/cat.rb +51 -0
- data/lib/cocoapods/command/spec/create.rb +283 -0
- data/lib/cocoapods/command/spec/edit.rb +87 -0
- data/lib/cocoapods/command/spec/env_spec.rb +53 -0
- data/lib/cocoapods/command/spec/lint.rb +137 -0
- data/lib/cocoapods/command/spec/which.rb +43 -0
- data/lib/cocoapods/command/update.rb +101 -0
- data/lib/cocoapods/config.rb +347 -0
- data/lib/cocoapods/core_overrides.rb +1 -0
- data/lib/cocoapods/downloader.rb +190 -0
- data/lib/cocoapods/downloader/cache.rb +233 -0
- data/lib/cocoapods/downloader/request.rb +86 -0
- data/lib/cocoapods/downloader/response.rb +16 -0
- data/lib/cocoapods/executable.rb +222 -0
- data/lib/cocoapods/external_sources.rb +57 -0
- data/lib/cocoapods/external_sources/abstract_external_source.rb +205 -0
- data/lib/cocoapods/external_sources/downloader_source.rb +30 -0
- data/lib/cocoapods/external_sources/path_source.rb +55 -0
- data/lib/cocoapods/external_sources/podspec_source.rb +54 -0
- data/lib/cocoapods/gem_version.rb +5 -0
- data/lib/cocoapods/generator/acknowledgements.rb +107 -0
- data/lib/cocoapods/generator/acknowledgements/markdown.rb +44 -0
- data/lib/cocoapods/generator/acknowledgements/plist.rb +94 -0
- data/lib/cocoapods/generator/app_target_helper.rb +244 -0
- data/lib/cocoapods/generator/bridge_support.rb +22 -0
- data/lib/cocoapods/generator/constant.rb +19 -0
- data/lib/cocoapods/generator/copy_resources_script.rb +230 -0
- data/lib/cocoapods/generator/dummy_source.rb +31 -0
- data/lib/cocoapods/generator/embed_frameworks_script.rb +215 -0
- data/lib/cocoapods/generator/header.rb +103 -0
- data/lib/cocoapods/generator/info_plist_file.rb +116 -0
- data/lib/cocoapods/generator/module_map.rb +99 -0
- data/lib/cocoapods/generator/prefix_header.rb +60 -0
- data/lib/cocoapods/generator/umbrella_header.rb +46 -0
- data/lib/cocoapods/hooks_manager.rb +132 -0
- data/lib/cocoapods/installer.rb +703 -0
- data/lib/cocoapods/installer/analyzer.rb +972 -0
- data/lib/cocoapods/installer/analyzer/analysis_result.rb +87 -0
- data/lib/cocoapods/installer/analyzer/locking_dependency_analyzer.rb +98 -0
- data/lib/cocoapods/installer/analyzer/pod_variant.rb +67 -0
- data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +157 -0
- data/lib/cocoapods/installer/analyzer/podfile_dependency_cache.rb +54 -0
- data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +240 -0
- data/lib/cocoapods/installer/analyzer/specs_state.rb +84 -0
- data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +53 -0
- data/lib/cocoapods/installer/analyzer/target_inspector.rb +260 -0
- data/lib/cocoapods/installer/installation_options.rb +158 -0
- data/lib/cocoapods/installer/pod_source_installer.rb +202 -0
- data/lib/cocoapods/installer/pod_source_preparer.rb +77 -0
- data/lib/cocoapods/installer/podfile_validator.rb +139 -0
- data/lib/cocoapods/installer/post_install_hooks_context.rb +132 -0
- data/lib/cocoapods/installer/pre_install_hooks_context.rb +51 -0
- data/lib/cocoapods/installer/source_provider_hooks_context.rb +34 -0
- data/lib/cocoapods/installer/user_project_integrator.rb +250 -0
- data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +463 -0
- data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +146 -0
- data/lib/cocoapods/installer/xcode.rb +8 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator.rb +416 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +181 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/app_host_installer.rb +84 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +334 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +777 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_integrator.rb +116 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/target_installation_result.rb +86 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +256 -0
- data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer_helper.rb +68 -0
- data/lib/cocoapods/installer/xcode/target_validator.rb +147 -0
- data/lib/cocoapods/open-uri.rb +33 -0
- data/lib/cocoapods/project.rb +414 -0
- data/lib/cocoapods/resolver.rb +585 -0
- data/lib/cocoapods/resolver/lazy_specification.rb +79 -0
- data/lib/cocoapods/sandbox.rb +404 -0
- data/lib/cocoapods/sandbox/file_accessor.rb +444 -0
- data/lib/cocoapods/sandbox/headers_store.rb +146 -0
- data/lib/cocoapods/sandbox/path_list.rb +220 -0
- data/lib/cocoapods/sandbox/pod_dir_cleaner.rb +85 -0
- data/lib/cocoapods/sandbox/podspec_finder.rb +23 -0
- data/lib/cocoapods/sources_manager.rb +157 -0
- data/lib/cocoapods/target.rb +261 -0
- data/lib/cocoapods/target/aggregate_target.rb +338 -0
- data/lib/cocoapods/target/build_settings.rb +1075 -0
- data/lib/cocoapods/target/pod_target.rb +559 -0
- data/lib/cocoapods/user_interface.rb +459 -0
- data/lib/cocoapods/user_interface/error_report.rb +187 -0
- data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
- data/lib/cocoapods/validator.rb +981 -0
- 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
|